From 63286f25d86e92e541e611092439d4b8daface85 Mon Sep 17 00:00:00 2001 From: gogerald Date: Mon, 14 Sep 2015 07:42:02 -0700 Subject: [PATCH] GAIA ID migration for Android. Much work has been done over the last year to prepare Chrome to use Gaia obfuscated Ids as the unique identifier for Google accounts in Chrome. This cl implements this work for Android platform. On Android platform, Google accounts' credentials are maintained in Android, Chrome list accounts name and get access tokens from Android. Chrome must use account name as identification to communicate with Android. In addition, Chrome can only fetch accounts' Gaia Ids through GoogleAuthUtil.getAccountId which is a blocked interface. These facts make Gaia Id migration a little bit challenge for Android. Please refer below doc for details. https://docs.google.com/a/google.com/document/d/1XPVUmpm3OFU8ZbYTSxOVtxb0zOLRnRXR1OR5Pp4ryb0/edit?usp=sharing BUG=341408 Review URL: https://codereview.chromium.org/1256283002 Cr-Commit-Position: refs/heads/master@{#348617} --- .../chrome/browser/firstrun/ProfileDataCache.java | 2 +- .../chrome/browser/profiles/ProfileDownloader.java | 74 ++++++++- .../browser/signin/AccountManagementFragment.java | 3 +- .../browser/signin/AccountTrackerService.java | 181 +++++++++++++++++++++ .../chrome/browser/signin/OAuth2TokenService.java | 57 ++++++- .../chrome/browser/signin/SigninHelper.java | 8 + .../chrome/browser/signin/SigninManager.java | 104 +++++------- .../signin/OAuth2TokenServiceIntegrationTest.java | 56 ++++++- .../browser/signin/OAuth2TokenServiceTest.java | 4 +- .../chromium/chrome/browser/sync/SyncTestBase.java | 18 +- chrome/browser/android/chrome_jni_registrar.cc | 2 + .../android/profiles/profile_downloader_android.cc | 16 +- .../signin/account_tracker_service_android.cc | 49 ++++++ .../signin/account_tracker_service_android.h | 36 ++++ .../android/signin/signin_manager_android.cc | 29 +--- .../android/signin/signin_manager_android.h | 7 +- .../cloud/user_policy_signin_service_mobile.cc | 12 +- .../cloud/user_policy_signin_service_mobile.h | 11 +- .../cloud/user_policy_signin_service_unittest.cc | 4 +- .../oauth2_token_service_delegate_android.cc | 144 ++++++++++++---- .../signin/oauth2_token_service_delegate_android.h | 25 ++- .../signin/profile_oauth2_token_service_factory.cc | 6 +- chrome/chrome_browser.gypi | 3 + .../cloud_policy_client_registration_helper.cc | 2 +- .../signin/core/browser/account_tracker_service.cc | 6 +- .../signin/core/browser/account_tracker_service.h | 2 + 26 files changed, 688 insertions(+), 173 deletions(-) create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/signin/AccountTrackerService.java create mode 100644 chrome/browser/android/signin/account_tracker_service_android.cc create mode 100644 chrome/browser/android/signin/account_tracker_service_android.h diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ProfileDataCache.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ProfileDataCache.java index d54e2fa548ec..21b87a80ee26 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ProfileDataCache.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ProfileDataCache.java @@ -94,7 +94,7 @@ public class ProfileDataCache implements Observer { for (int i = 0; i < accounts.length; i++) { if (mCacheEntries.get(accounts[i].name) == null) { ProfileDownloader.startFetchingAccountInfoFor( - mProfile, accounts[i].name, mImageSizePx, true); + mContext, mProfile, accounts[i].name, mImageSizePx, true); } } } diff --git a/chrome/android/java/src/org/chromium/chrome/browser/profiles/ProfileDownloader.java b/chrome/android/java/src/org/chromium/chrome/browser/profiles/ProfileDownloader.java index 286e99f1520d..3ac59f70cfa8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/profiles/ProfileDownloader.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/profiles/ProfileDownloader.java @@ -4,11 +4,16 @@ package org.chromium.chrome.browser.profiles; +import android.content.Context; import android.graphics.Bitmap; import org.chromium.base.ObserverList; import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.SuppressFBWarnings; +import org.chromium.chrome.browser.signin.AccountTrackerService; + +import java.util.ArrayList; /** * Android wrapper of the ProfileDownloader which provides access from the Java layer. @@ -50,14 +55,79 @@ public class ProfileDownloader { } /** + * Private class to pend profile download requests when system accounts have not been seeded into + * AccountTrackerService. It listens onSystemAccountsSeedingComplete to finish pending requests + * and onSystemAccountsForceRefreshed to clear outdated pending requests. + */ + private static class PendingProfileDownloads + implements AccountTrackerService.OnSystemAccountsSeededListener { + private static PendingProfileDownloads sPendingProfileDownloads; + + private final ArrayList mProfiles; + private final ArrayList mAccountIds; + private final ArrayList mImageSidePixels; + + private PendingProfileDownloads() { + mProfiles = new ArrayList<>(); + mAccountIds = new ArrayList<>(); + mImageSidePixels = new ArrayList<>(); + } + + @SuppressFBWarnings("LI_LAZY_INIT_UPDATE_STATIC") + public static PendingProfileDownloads get(Context context) { + ThreadUtils.assertOnUiThread(); + if (sPendingProfileDownloads == null) { + sPendingProfileDownloads = new PendingProfileDownloads(); + AccountTrackerService.get(context).addSystemAccountsSeededListener( + sPendingProfileDownloads); + } + return sPendingProfileDownloads; + } + + public void pendProfileDownload(Profile profile, String accountId, int imageSidePixels) { + mProfiles.add(profile); + mAccountIds.add(accountId); + mImageSidePixels.add(imageSidePixels); + } + + @Override + public void onSystemAccountsSeedingComplete() { + int numberOfPendingRequests = mAccountIds.size(); + while (numberOfPendingRequests > 0) { + // Pending requests here must be pre-signin request since SigninManager will wait + // system accounts been seeded into AccountTrackerService before finishing sign in. + nativeStartFetchingAccountInfoFor( + mProfiles.get(0), mAccountIds.get(0), mImageSidePixels.get(0), true); + mProfiles.remove(0); + mAccountIds.remove(0); + mImageSidePixels.remove(0); + numberOfPendingRequests--; + } + } + + @Override + public void onSystemAccountsForceRefreshed() { + mProfiles.clear(); + mAccountIds.clear(); + mImageSidePixels.clear(); + } + } + + /** * Starts fetching the account information for a given account. + * @param context context associated with the request * @param profile Profile associated with the request * @param accountId Account name to fetch the information for * @param imageSidePixels Request image side (in pixels) */ - public static void startFetchingAccountInfoFor( - Profile profile, String accountId, int imageSidePixels, boolean isPreSignin) { + public static void startFetchingAccountInfoFor(Context context, Profile profile, + String accountId, int imageSidePixels, boolean isPreSignin) { ThreadUtils.assertOnUiThread(); + if (!AccountTrackerService.get(context).isSystemAccountsSeeded()) { + PendingProfileDownloads.get(context).pendProfileDownload( + profile, accountId, imageSidePixels); + return; + } nativeStartFetchingAccountInfoFor(profile, accountId, imageSidePixels, isPreSignin); } diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountManagementFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountManagementFragment.java index bba1000ca105..d234262c66ba 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountManagementFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountManagementFragment.java @@ -676,7 +676,8 @@ public class AccountManagementFragment extends PreferenceFragment final int imageSidePixels = context.getResources().getDimensionPixelOffset(R.dimen.user_picture_size); - ProfileDownloader.startFetchingAccountInfoFor(profile, accountName, imageSidePixels, false); + ProfileDownloader.startFetchingAccountInfoFor( + context, profile, accountName, imageSidePixels, false); } /** diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountTrackerService.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountTrackerService.java new file mode 100644 index 000000000000..e422ac25aec7 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountTrackerService.java @@ -0,0 +1,181 @@ +// Copyright 2015 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.signin; + +import android.content.Context; +import android.os.AsyncTask; + +import org.chromium.base.Log; +import org.chromium.base.ObserverList; +import org.chromium.base.ThreadUtils; +import org.chromium.base.VisibleForTesting; +import org.chromium.sync.signin.AccountManagerHelper; + +import java.util.List; + +/** +* Android wrapper of AccountTrackerService which provides access from the java layer. +* It offers the capability of fetching and seeding system accounts into AccountTrackerService in C++ +* layer, and notifies observers when it is complete. +*/ +public class AccountTrackerService { + private static final String TAG = "cr.AccountService"; + private static AccountTrackerService sAccountTrackerService; + private static AccountIdProvider sAccountIdProvider; + private SystemAccountsSeedingStatus mSystemAccountsSeedingStatus = + SystemAccountsSeedingStatus.SEEDING_NOT_STARTED; + private boolean mForceRefresh; + private boolean mSyncForceRefreshedForTest; + + private final Context mContext; + private final long mNativeAccountTrackerService; + + private enum SystemAccountsSeedingStatus { + SEEDING_NOT_STARTED, + SEEDING_IN_PROGRESS, + SEEDING_DONE + } + + /** + * Classes that want to listen for system accounts fetching and seeding should implement + * this interface and register with {@link #addSystemAccountsSeededListener}. + */ + public interface OnSystemAccountsSeededListener { + // Called at the end of seedSystemAccounts(). + void onSystemAccountsSeedingComplete(); + // Called at the beginning of system accounts being force refreshed. + void onSystemAccountsForceRefreshed(); + } + + private final ObserverList mSystemAccountsSeedingObservers = + new ObserverList<>(); + + public static AccountTrackerService get(Context context) { + ThreadUtils.assertOnUiThread(); + if (sAccountTrackerService == null) { + sAccountTrackerService = new AccountTrackerService(context); + sAccountIdProvider = AccountIdProvider.getInstance(); + } + return sAccountTrackerService; + } + + private AccountTrackerService(Context context) { + mContext = context; + mNativeAccountTrackerService = nativeInit(); + } + + /** + * Check whether system accounts have been seeded into AccountTrackerService in C++ layer. + */ + public boolean isSystemAccountsSeeded() { + ThreadUtils.assertOnUiThread(); + if (mSystemAccountsSeedingStatus == SystemAccountsSeedingStatus.SEEDING_DONE) { + return true; + } + + if (mSystemAccountsSeedingStatus == SystemAccountsSeedingStatus.SEEDING_NOT_STARTED) { + seedSystemAccounts(); + } + + return false; + } + + /** + * Register an |observer| to observe system accounts seeding status. + */ + public void addSystemAccountsSeededListener(OnSystemAccountsSeededListener observer) { + ThreadUtils.assertOnUiThread(); + mSystemAccountsSeedingObservers.addObserver(observer); + if (mSystemAccountsSeedingStatus == SystemAccountsSeedingStatus.SEEDING_DONE) { + observer.onSystemAccountsSeedingComplete(); + } + } + + private void seedSystemAccounts() { + ThreadUtils.assertOnUiThread(); + mSystemAccountsSeedingStatus = SystemAccountsSeedingStatus.SEEDING_IN_PROGRESS; + mForceRefresh = false; + mSyncForceRefreshedForTest = false; + AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(mContext); + List accountNamesList = accountManagerHelper.getGoogleAccountNames(); + final String[] accountNames = accountNamesList.toArray(new String[accountNamesList.size()]); + new AsyncTask() { + @Override + public String[] doInBackground(Void... params) { + Log.d(TAG, "Getting id/email mapping"); + String[] accountIds = new String[accountNames.length]; + for (int i = 0; i < accountIds.length; ++i) { + accountIds[i] = sAccountIdProvider.getAccountId(mContext, accountNames[i]); + } + return accountIds; + } + @Override + public void onPostExecute(String[] accountIds) { + if (mSyncForceRefreshedForTest) return; + if (mForceRefresh) { + seedSystemAccounts(); + return; + } + if (areAccountIdsValid(accountIds)) { + nativeSeedAccountsInfo(mNativeAccountTrackerService, accountIds, accountNames); + mSystemAccountsSeedingStatus = SystemAccountsSeedingStatus.SEEDING_DONE; + notifyObserversOnSeedingComplete(); + } else { + Log.w(TAG, "Invalid mapping of id/email"); + seedSystemAccounts(); + } + } + }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + } + + private boolean areAccountIdsValid(String[] accountIds) { + for (int i = 0; i < accountIds.length; ++i) { + if (accountIds[i] == null) return false; + } + return true; + } + + private void notifyObserversOnSeedingComplete() { + for (OnSystemAccountsSeededListener observer : mSystemAccountsSeedingObservers) { + observer.onSystemAccountsSeedingComplete(); + } + } + + /** + * Seed system accounts into AccountTrackerService synchronously for test purpose. + */ + @VisibleForTesting + public void syncForceRefreshForTest(String[] accountIds, String[] accountNames) { + ThreadUtils.assertOnUiThread(); + mSystemAccountsSeedingStatus = SystemAccountsSeedingStatus.SEEDING_IN_PROGRESS; + mForceRefresh = false; + mSyncForceRefreshedForTest = true; + nativeSeedAccountsInfo(mNativeAccountTrackerService, accountIds, accountNames); + mSystemAccountsSeedingStatus = SystemAccountsSeedingStatus.SEEDING_DONE; + } + + /** + * Force AccountTrackerService refresh/update systems accounts. + */ + public void forceRefresh() { + ThreadUtils.assertOnUiThread(); + mForceRefresh = true; + notifyObserversOnForceRefreshed(); + if (mSystemAccountsSeedingStatus != SystemAccountsSeedingStatus.SEEDING_IN_PROGRESS) { + seedSystemAccounts(); + } + } + + private void notifyObserversOnForceRefreshed() { + for (OnSystemAccountsSeededListener observer : mSystemAccountsSeedingObservers) { + observer.onSystemAccountsForceRefreshed(); + } + } + + // Native methods. + private native long nativeInit(); + private native void nativeSeedAccountsInfo( + long nativeAccountTrackerServiceAndroid, String[] gaiaIds, String[] accountNames); +} diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/OAuth2TokenService.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/OAuth2TokenService.java index 2ca4be96e58c..fb161a9b17c5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/signin/OAuth2TokenService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/OAuth2TokenService.java @@ -34,8 +34,8 @@ import javax.annotation.Nullable; * AccountManagerHelper and forwards callbacks to native code. *

*/ -public final class OAuth2TokenService { - +public final class OAuth2TokenService + implements AccountTrackerService.OnSystemAccountsSeededListener { private static final String TAG = "OAuth2TokenService"; @VisibleForTesting @@ -53,12 +53,16 @@ public final class OAuth2TokenService { private static final String OAUTH2_SCOPE_PREFIX = "oauth2:"; + private Context mPendingValidationContext = null; + private boolean mPendingValidationForceNotifications = false; + private final long mNativeOAuth2TokenServiceDelegateAndroid; private final ObserverList mObservers; - private OAuth2TokenService(long nativeOAuth2Service) { + private OAuth2TokenService(Context context, long nativeOAuth2Service) { mNativeOAuth2TokenServiceDelegateAndroid = nativeOAuth2Service; mObservers = new ObserverList(); + AccountTrackerService.get(context).addSystemAccountsSeededListener(this); } public static OAuth2TokenService getForProfile(Profile profile) { @@ -67,9 +71,9 @@ public final class OAuth2TokenService { } @CalledByNative - private static OAuth2TokenService create(long nativeOAuth2Service) { + private static OAuth2TokenService create(Context context, long nativeOAuth2Service) { ThreadUtils.assertOnUiThread(); - return new OAuth2TokenService(nativeOAuth2Service); + return new OAuth2TokenService(context, nativeOAuth2Service); } @VisibleForTesting @@ -100,19 +104,19 @@ public final class OAuth2TokenService { } /** - * Called by native to list the activite accounts in the OS. + * Called by native to list the activite account names in the OS. */ @VisibleForTesting @CalledByNative - public static String[] getSystemAccounts(Context context) { + public static String[] getSystemAccountNames(Context context) { AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context); java.util.List accountNames = accountManagerHelper.getGoogleAccountNames(); return accountNames.toArray(new String[accountNames.size()]); } /** - * Called by native to list the accounts with OAuth2 refresh tokens. - * This can differ from getSystemAccounts as the user add/remove accounts + * Called by native to list the accounts Id with OAuth2 refresh tokens. + * This can differ from getSystemAccountNames as the user add/remove accounts * from the OS. validateAccounts should be called to keep these two * in sync. */ @@ -229,9 +233,44 @@ public final class OAuth2TokenService { } } + /** + * Continue pending accounts validation after system accounts have been seeded into + * AccountTrackerService. + */ + @Override + public void onSystemAccountsSeedingComplete() { + if (mPendingValidationContext != null) { + validateAccountsWithSignedInAccountName( + mPendingValidationContext, mPendingValidationForceNotifications); + mPendingValidationContext = null; + mPendingValidationForceNotifications = false; + } + } + + /** + * Clear pending accounts validation when system accounts in AccountTrackerService were + * refreshed. + */ + @Override + public void onSystemAccountsForceRefreshed() { + mPendingValidationContext = null; + mPendingValidationForceNotifications = false; + } + @CalledByNative public void validateAccounts(Context context, boolean forceNotifications) { ThreadUtils.assertOnUiThread(); + if (!AccountTrackerService.get(context).isSystemAccountsSeeded()) { + mPendingValidationContext = context; + mPendingValidationForceNotifications = forceNotifications; + return; + } + + validateAccountsWithSignedInAccountName(context, forceNotifications); + } + + private void validateAccountsWithSignedInAccountName( + Context context, boolean forceNotifications) { String currentlySignedInAccount = ChromeSigninController.get(context).getSignedInAccountName(); nativeValidateAccounts(mNativeOAuth2TokenServiceDelegateAndroid, currentlySignedInAccount, diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelper.java index 8203c303cb36..b00537b476ba 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelper.java @@ -107,6 +107,8 @@ public class SigninHelper { private final SigninManager mSigninManager; + private final AccountTrackerService mAccountTrackerService; + private final OAuth2TokenService mOAuth2TokenService; private final SyncController mSyncController; @@ -124,12 +126,18 @@ public class SigninHelper { mContext = context; mProfileSyncService = ProfileSyncService.get(); mSigninManager = SigninManager.get(mContext); + mAccountTrackerService = AccountTrackerService.get(mContext); mOAuth2TokenService = OAuth2TokenService.getForProfile(Profile.getLastUsedProfile()); mSyncController = SyncController.get(context); mChromeSigninController = ChromeSigninController.get(mContext); } public void validateAccountSettings(boolean accountsChanged) { + if (accountsChanged) { + // Account details have changed so inform AccountTrackerService refresh itself. + mAccountTrackerService.forceRefresh(); + } + Account syncAccount = mChromeSigninController.getSignedInUser(); if (syncAccount == null) { if (SigninManager.getAndroidSigninPromoExperimentGroup() < 0) return; diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java index 67365f107f2a..92640b395efa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java @@ -11,7 +11,6 @@ import android.app.DialogFragment; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AlertDialog; @@ -43,8 +42,7 @@ import javax.annotation.Nullable; *

* See chrome/browser/signin/signin_manager_android.h for more details. */ -public class SigninManager { - +public class SigninManager implements AccountTrackerService.OnSystemAccountsSeededListener { public static final String CONFIRM_MANAGED_SIGNIN_DIALOG_TAG = "confirm_managed_signin_dialog_tag"; @@ -97,6 +95,8 @@ public class SigninManager { private ConfirmManagedSigninFragment mPolicyConfirmationDialog; + private boolean mHasPendingSignin = false; + private boolean mSigninAllowedByPolicy; /** @@ -143,28 +143,6 @@ public class SigninManager { } /** - * Structure used to pass account ids and names from a background async task to the - * foreground post execute function. This structure contains two arrays of the same - * length: one containing strings of stable account ids and the other containing - * strings of account names (or emails). An account id corresponds with the account - * name at the same position in the array. - */ - private static class AccountIdsAndNames { - public final String[] mAccountIds; - public final String[] mAccountNames; - - public AccountIdsAndNames(String[] accountIds, String[] accountNames) { - // Make sure that both arrays arguments are either null or have the same length. - assert (accountIds == null) == (accountNames == null); - if (accountIds != null && accountNames != null) { - assert accountIds.length == accountNames.length; - } - mAccountIds = accountIds; - mAccountNames = accountNames; - } - } - - /** * A helper method for retrieving the application-wide SigninManager. *

* Can only be accessed on the main thread. @@ -192,6 +170,7 @@ public class SigninManager { mSigninNotificationController = new SigninNotificationController( mContext, controller, AccountManagementFragment.class); ChromeSigninController.get(mContext).addListener(mSigninNotificationController); + AccountTrackerService.get(mContext).addSystemAccountsSeededListener(this); } /** @@ -265,6 +244,28 @@ public class SigninManager { } /** + * Continue pending sign in after system accounts have been seeded into AccountTrackerService. + */ + @Override + public void onSystemAccountsSeedingComplete() { + if (mHasPendingSignin) { + mHasPendingSignin = false; + doSignIn(); + } + } + + /** + * Clear pending sign in when system accounts in AccountTrackerService were refreshed. + */ + @Override + public void onSystemAccountsForceRefreshed() { + if (mHasPendingSignin) { + mHasPendingSignin = false; + cancelSignIn(); + } + } + + /** * Starts the sign-in flow, and executes the callback when ready to proceed. *

* This method checks with the native side whether the account has management enabled, and may @@ -295,28 +296,32 @@ public class SigninManager { notifySignInAllowedChanged(); - if (!AccountIdProvider.getInstance().canBeUsed(mContext, mSignInActivity)) { - Log.d(TAG, "Deferring the sign-in process as Google Play services is unavailable"); + if (!AccountTrackerService.get(mContext).isSystemAccountsSeeded()) { + mHasPendingSignin = true; return; } - if (!nativeShouldLoadPolicyForUser(account.name)) { + doSignIn(); + } + + private void doSignIn() { + if (!nativeShouldLoadPolicyForUser(mSignInAccount.name)) { // Proceed with the sign-in flow without checking for policy if it can be determined // that this account can't have management enabled based on the username. - doSignIn(); + finishSignIn(); return; } Log.d(TAG, "Checking if account has policy management enabled"); // This will call back to onPolicyCheckedBeforeSignIn. - nativeCheckPolicyBeforeSignIn(mNativeSigninManagerAndroid, account.name); + nativeCheckPolicyBeforeSignIn(mNativeSigninManagerAndroid, mSignInAccount.name); } @CalledByNative private void onPolicyCheckedBeforeSignIn(String managementDomain) { if (managementDomain == null) { Log.d(TAG, "Account doesn't have policy"); - doSignIn(); + finishSignIn(); return; } @@ -366,43 +371,17 @@ public class SigninManager { private void onPolicyFetchedBeforeSignIn() { // Policy has been fetched for the user and is being enforced; features like sync may now // be disabled by policy, and the rest of the sign-in flow can be resumed. - doSignIn(); + finishSignIn(); } - private void doSignIn() { - Log.d(TAG, "Committing the sign-in process now"); - assert mSignInAccount != null; - - // Get mapping from account names to account ids. - final AccountIdProvider provider = AccountIdProvider.getInstance(); - new AsyncTask() { - @Override - public AccountIdsAndNames doInBackground(Void... params) { - Log.d(TAG, "Getting id/email mapping"); - String[] accountNames = OAuth2TokenService.getSystemAccounts(mContext); - assert accountNames.length > 0; - String[] accountIds = new String[accountNames.length]; - for (int i = 0; i < accountIds.length; ++i) { - accountIds[i] = provider.getAccountId(mContext, accountNames[i]); - } - return new AccountIdsAndNames(accountIds, accountNames); - } - @Override - public void onPostExecute(AccountIdsAndNames accountIdsAndNames) { - finishSignIn(accountIdsAndNames); - } - }.execute(); - } - - private void finishSignIn(AccountIdsAndNames accountIdsAndNames) { + private void finishSignIn() { if (mSignInAccount == null) { Log.w(TAG, "Sign in request was canceled; aborting finishSignIn()."); return; } // Tell the native side that sign-in has completed. - nativeOnSignInCompleted(mNativeSigninManagerAndroid, mSignInAccount.name, - accountIdsAndNames.mAccountIds, accountIdsAndNames.mAccountNames); + nativeOnSignInCompleted(mNativeSigninManagerAndroid, mSignInAccount.name); // Cache the signed-in account name. This must be done after the native call, otherwise // sync tries to start without being signed in natively and crashes. @@ -456,6 +435,8 @@ public class SigninManager { } else { onSignOutDone(); } + + AccountTrackerService.get(mContext).forceRefresh(); } /** @@ -628,8 +609,7 @@ public class SigninManager { private native void nativeCheckPolicyBeforeSignIn( long nativeSigninManagerAndroid, String username); private native void nativeFetchPolicyBeforeSignIn(long nativeSigninManagerAndroid); - private native void nativeOnSignInCompleted(long nativeSigninManagerAndroid, String username, - String[] accountIds, String[] accountNames); + private native void nativeOnSignInCompleted(long nativeSigninManagerAndroid, String username); private native void nativeSignOut(long nativeSigninManagerAndroid); private native String nativeGetManagementDomain(long nativeSigninManagerAndroid); private native void nativeWipeProfileData(long nativeSigninManagerAndroid); diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java index 2e5bff09807d..c5b1e3db56fd 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java @@ -5,6 +5,8 @@ package org.chromium.chrome.browser.signin; import android.accounts.Account; +import android.app.Activity; +import android.content.Context; import android.test.UiThreadTest; import android.test.suitebuilder.annotation.MediumTest; @@ -45,6 +47,7 @@ public class OAuth2TokenServiceIntegrationTest extends NativeLibraryTestBase { @Override protected void setUp() throws Exception { + mapAccountNamesToIds(); super.setUp(); ApplicationData.clearAppData(getInstrumentation().getTargetContext()); loadNativeLibraryAndInitBrowserProcess(); @@ -58,6 +61,9 @@ public class OAuth2TokenServiceIntegrationTest extends NativeLibraryTestBase { mChromeSigninController = ChromeSigninController.get(mContext); mChromeSigninController.setSignedInAccountName(null); + // Seed test accounts to AccountTrackerService. + seedAccountTrackerService(mContext); + // Get a reference to the service. mOAuth2TokenService = getOAuth2TokenServiceOnUiThread(); @@ -66,6 +72,51 @@ public class OAuth2TokenServiceIntegrationTest extends NativeLibraryTestBase { addObserver(mObserver); } + @Override + protected void tearDown() throws Exception { + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + mChromeSigninController.setSignedInAccountName(null); + mOAuth2TokenService.validateAccounts(mContext, false); + } + }); + super.tearDown(); + } + + private void mapAccountNamesToIds() { + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + AccountIdProvider.setInstanceForTest(new AccountIdProvider() { + @Override + public String getAccountId(Context ctx, String accountName) { + return "gaia-id-" + accountName; + } + + @Override + public boolean canBeUsed(Context ctx, Activity activity) { + return true; + } + }); + } + }); + } + + private void seedAccountTrackerService(final Context context) { + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + AccountIdProvider provider = AccountIdProvider.getInstance(); + String[] accountNames = {TEST_ACCOUNT1.name, TEST_ACCOUNT2.name}; + String[] accountIds = {provider.getAccountId(context, accountNames[0]), + provider.getAccountId(context, accountNames[1])}; + AccountTrackerService.get(context).syncForceRefreshForTest( + accountIds, accountNames); + } + }); + } + /** * The {@link OAuth2TokenService} and the {@link Profile} can only be accessed from the UI * thread, so this helper method is a convenience method to retrieve it. @@ -129,7 +180,7 @@ public class OAuth2TokenServiceIntegrationTest extends NativeLibraryTestBase { // When removed, an observer should not be called. mOAuth2TokenService.removeObserver(mObserver); - mOAuth2TokenService.fireRefreshTokenRevoked(TEST_ACCOUNT1); + mOAuth2TokenService.fireRefreshTokenRevoked(TEST_ACCOUNT2); assertEquals(1, mObserver.getRevokedCallCount()); // No other observer interface method should ever have been called. @@ -255,6 +306,9 @@ public class OAuth2TokenServiceIntegrationTest extends NativeLibraryTestBase { // Add another account. mAccountManager.addAccountHolderExplicitly(TEST_ACCOUNT_HOLDER_2); + // Seed AccountTrackerService again since accounts changed after last validation. + seedAccountTrackerService(mContext); + // Re-run validation. mOAuth2TokenService.validateAccounts(mContext, false); assertEquals(2, mObserver.getAvailableCallCount()); diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java index 33f27fe63d0e..bff6c60cb171 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java @@ -49,7 +49,7 @@ public class OAuth2TokenServiceTest extends InstrumentationTestCase { AccountHolder accountHolder1 = AccountHolder.create().account(account1).build(); mAccountManager.addAccountHolderExplicitly(accountHolder1); - String[] sysAccounts = OAuth2TokenService.getSystemAccounts(mContext); + String[] sysAccounts = OAuth2TokenService.getSystemAccountNames(mContext); assertEquals("There should be one registered account", 1, sysAccounts.length); assertEquals("The account should be " + account1, account1.name, sysAccounts[0]); @@ -69,7 +69,7 @@ public class OAuth2TokenServiceTest extends InstrumentationTestCase { AccountHolder accountHolder2 = AccountHolder.create().account(account2).build(); mAccountManager.addAccountHolderExplicitly(accountHolder2); - String[] sysAccounts = OAuth2TokenService.getSystemAccounts(mContext); + String[] sysAccounts = OAuth2TokenService.getSystemAccountNames(mContext); assertEquals("There should be one registered account", 2, sysAccounts.length); assertTrue("The list should contain " + account1, Arrays.asList(sysAccounts).contains(account1.name)); diff --git a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java index efa4a3862947..062c260becc3 100644 --- a/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java +++ b/chrome/android/sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java @@ -14,6 +14,7 @@ import org.chromium.chrome.browser.identity.UniqueIdentificationGenerator; import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory; import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerator; import org.chromium.chrome.browser.signin.AccountIdProvider; +import org.chromium.chrome.browser.signin.AccountTrackerService; import org.chromium.chrome.browser.signin.SigninManager; import org.chromium.chrome.test.ChromeActivityTestCaseBase; import org.chromium.chrome.test.util.browser.sync.SyncTestUtil; @@ -39,6 +40,7 @@ public class SyncTestBase extends ChromeActivityTestCaseBase { protected FakeServerHelper mFakeServerHelper; protected ProfileSyncService mProfileSyncService; protected MockSyncContentResolverDelegate mSyncContentResolver; + protected ChromeSigninController mChromeSigninController; public SyncTestBase() { super(ChromeActivity.class); @@ -64,8 +66,10 @@ public class SyncTestBase extends ChromeActivityTestCaseBase { setUpMockAndroidSyncSettings(); setUpMockAccountManager(); - // Initializes ChromeSigninController to use our test context. - ChromeSigninController.get(mContext); + // Initializes ChromeSigninController to use our test context and make sure there is no + // account is signed in yet. + mChromeSigninController = ChromeSigninController.get(mContext); + mChromeSigninController.setSignedInAccountName(null); ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override @@ -95,6 +99,8 @@ public class SyncTestBase extends ChromeActivityTestCaseBase { } }); + if (mChromeSigninController.isSignedIn()) signOut(); + super.tearDown(); } @@ -133,6 +139,14 @@ public class SyncTestBase extends ChromeActivityTestCaseBase { Account defaultTestAccount = SyncTestUtil.setupTestAccountThatAcceptsAllAuthTokens( mAccountManager, SyncTestUtil.DEFAULT_TEST_ACCOUNT, SyncTestUtil.DEFAULT_PASSWORD); + // Force refresh AccountTrackerService since accounts are changed. + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + AccountTrackerService.get(mContext).forceRefresh(); + } + }); + UniqueIdentificationGeneratorFactory.registerGenerator( UuidBasedUniqueIdentificationGenerator.GENERATOR_ID, new UniqueIdentificationGenerator() { diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc index f20be0eb41ab..512a8c3273c9 100644 --- a/chrome/browser/android/chrome_jni_registrar.cc +++ b/chrome/browser/android/chrome_jni_registrar.cc @@ -70,6 +70,7 @@ #include "chrome/browser/android/rlz/revenue_stats.h" #include "chrome/browser/android/shortcut_helper.h" #include "chrome/browser/android/signin/account_management_screen_helper.h" +#include "chrome/browser/android/signin/account_tracker_service_android.h" #include "chrome/browser/android/signin/signin_manager_android.h" #include "chrome/browser/android/tab/thumbnail_tab_helper_android.h" #include "chrome/browser/android/tab_android.h" @@ -176,6 +177,7 @@ static base::android::RegistrationMethod kChromeRegisteredMethods[] = { {"AccessibilityUtils", AccessibilityUtil::Register}, {"AccountChooserInfoBar", RegisterAccountChooserInfoBar}, {"AccountManagementScreenHelper", AccountManagementScreenHelper::Register}, + {"AccountTrackerService", AccountTrackerServiceAndroid::Register}, {"AddToHomescreenDialogHelper", AddToHomescreenDialogHelper::RegisterAddToHomescreenDialogHelper}, {"AfterStartupTaskUtils", RegisterAfterStartupTaskUtilsJNI}, diff --git a/chrome/browser/android/profiles/profile_downloader_android.cc b/chrome/browser/android/profiles/profile_downloader_android.cc index f2162d0b461c..9ff6f4df4393 100644 --- a/chrome/browser/android/profiles/profile_downloader_android.cc +++ b/chrome/browser/android/profiles/profile_downloader_android.cc @@ -187,15 +187,13 @@ void StartFetchingAccountInfoFor(JNIEnv* env, Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile); const std::string email = base::android::ConvertJavaStringToUTF8(env, jemail); - // TODO(rogerta): the java code will need to pass in the gaia-id - // of the account instead of the email when chrome uses gaia-id as key. - DCHECK_EQ(AccountTrackerService::MIGRATION_NOT_STARTED, - AccountTrackerServiceFactory::GetForProfile(profile)-> - GetMigrationState()); - AccountInfoRetriever* retriever = - new AccountInfoRetriever( - profile, gaia::CanonicalizeEmail(gaia::SanitizeEmail(email)), email, - image_side_pixels, is_pre_signin); + AccountTrackerService* account_tracker_service = + AccountTrackerServiceFactory::GetForProfile(profile); + + AccountInfoRetriever* retriever = new AccountInfoRetriever( + profile, + account_tracker_service->FindAccountInfoByEmail(email).account_id, email, + image_side_pixels, is_pre_signin); retriever->Start(); } diff --git a/chrome/browser/android/signin/account_tracker_service_android.cc b/chrome/browser/android/signin/account_tracker_service_android.cc new file mode 100644 index 000000000000..26a2590c2742 --- /dev/null +++ b/chrome/browser/android/signin/account_tracker_service_android.cc @@ -0,0 +1,49 @@ +// Copyright 2015 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. + +#include "chrome/browser/android/signin/account_tracker_service_android.h" + +#include "base/android/jni_array.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/signin/account_tracker_service_factory.h" +#include "components/signin/core/browser/account_info.h" +#include "jni/AccountTrackerService_jni.h" + +AccountTrackerServiceAndroid::AccountTrackerServiceAndroid(JNIEnv* env, + jobject obj) { + java_account_tracker_service_.Reset(env, obj); +} + +void AccountTrackerServiceAndroid::SeedAccountsInfo(JNIEnv* env, + jobject obj, + jobjectArray gaiaIds, + jobjectArray accountNames) { + std::vector gaia_ids; + std::vector account_names; + base::android::AppendJavaStringArrayToStringVector(env, gaiaIds, &gaia_ids); + base::android::AppendJavaStringArrayToStringVector(env, accountNames, + &account_names); + DCHECK_EQ(gaia_ids.size(), account_names.size()); + + DVLOG(1) << "AccountTrackerServiceAndroid::SeedAccountsInfo: " + << " number of accounts " << gaia_ids.size(); + Profile* profile = ProfileManager::GetActiveUserProfile(); + AccountTrackerService* account_tracker_service_ = + AccountTrackerServiceFactory::GetForProfile(profile); + + for (unsigned int i = 0; i < gaia_ids.size(); i++) { + account_tracker_service_->SeedAccountInfo(gaia_ids[i], account_names[i]); + } +} + +static jlong Init(JNIEnv* env, const JavaParamRef& obj) { + AccountTrackerServiceAndroid* account_tracker_service_android = + new AccountTrackerServiceAndroid(env, obj); + return reinterpret_cast(account_tracker_service_android); +} + +// static +bool AccountTrackerServiceAndroid::Register(JNIEnv* env) { + return RegisterNativesImpl(env); +} diff --git a/chrome/browser/android/signin/account_tracker_service_android.h b/chrome/browser/android/signin/account_tracker_service_android.h new file mode 100644 index 000000000000..1770b1f1eba3 --- /dev/null +++ b/chrome/browser/android/signin/account_tracker_service_android.h @@ -0,0 +1,36 @@ +// Copyright 2015 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. + +#ifndef CHROME_BROWSER_ANDROID_SIGNIN_ACCOUNT_TRACKER_SERVICE_ANDROID_H_ +#define CHROME_BROWSER_ANDROID_SIGNIN_ACCOUNT_TRACKER_SERVICE_ANDROID_H_ + +#include + +#include "base/android/scoped_java_ref.h" +#include "chrome/browser/profiles/profile.h" +#include "components/signin/core/browser/account_tracker_service.h" + +// Android wrapper of the AccountTrackerService which provides access from Java +// layer. Note that on Android, there's only a single profile, and therefore a +// single instance of this wrapper. The same name of the Java class is +// AccountTrackerService. +class AccountTrackerServiceAndroid { + public: + AccountTrackerServiceAndroid(JNIEnv* env, jobject obj); + + // Registers the AccountTrackerServiceAndroid's native methods through JNI. + static bool Register(JNIEnv* env); + + void SeedAccountsInfo(JNIEnv* env, + jobject obj, + jobjectArray gaiaIds, + jobjectArray accountNames); + + private: + ~AccountTrackerServiceAndroid(); + + base::android::ScopedJavaGlobalRef java_account_tracker_service_; +}; + +#endif // CHROME_BROWSER_ANDROID_SIGNIN_ACCOUNT_TRACKER_SERVICE_ANDROID_H_ diff --git a/chrome/browser/android/signin/signin_manager_android.cc b/chrome/browser/android/signin/signin_manager_android.cc index e07820688192..439f0acd5496 100644 --- a/chrome/browser/android/signin/signin_manager_android.cc +++ b/chrome/browser/android/signin/signin_manager_android.cc @@ -104,7 +104,9 @@ void SigninManagerAndroid::CheckPolicyBeforeSignIn(JNIEnv* env, policy::UserPolicySigninService* service = policy::UserPolicySigninServiceFactory::GetForProfile(profile_); service->RegisterForPolicy( - base::android::ConvertJavaStringToUTF8(env, username), + username_, AccountTrackerServiceFactory::GetForProfile(profile_) + ->FindAccountInfoByEmail(username_) + .account_id, base::Bind(&SigninManagerAndroid::OnPolicyRegisterDone, weak_factory_.GetWeakPtr())); #else @@ -143,31 +145,8 @@ void SigninManagerAndroid::FetchPolicyBeforeSignIn(JNIEnv* env, jobject obj) { void SigninManagerAndroid::OnSignInCompleted(JNIEnv* env, jobject obj, - jstring username, - jobjectArray accountIds, - jobjectArray accountNames) { + jstring username) { DVLOG(1) << "SigninManagerAndroid::OnSignInCompleted"; - // Seed the account tracker with id/email information if provided. - DCHECK(accountIds && accountNames); - std::vector gaia_ids; - std::vector emails; - base::android::AppendJavaStringArrayToStringVector(env, accountIds, - &gaia_ids); - base::android::AppendJavaStringArrayToStringVector(env, accountNames, - &emails); - DCHECK_EQ(emails.size(), gaia_ids.size()); - DVLOG(1) << "SigninManagerAndroid::OnSignInCompleted: seeding " - << emails.size() << " accounts"; - - AccountTrackerService* tracker = - AccountTrackerServiceFactory::GetForProfile(profile_); - for (size_t i = 0; i < emails.size(); ++i) { - DVLOG(1) << "SigninManagerAndroid::OnSignInCompleted: seeding" - << " gaia_id=" << gaia_ids[i] << " email=" << emails[i]; - if (!gaia_ids[i].empty() && !emails[i].empty()) - tracker->SeedAccountInfo(gaia_ids[i], emails[i]); - } - SigninManagerFactory::GetForProfile(profile_)->OnExternalSigninCompleted( base::android::ConvertJavaStringToUTF8(env, username)); } diff --git a/chrome/browser/android/signin/signin_manager_android.h b/chrome/browser/android/signin/signin_manager_android.h index 876329a30c43..17c100053fa7 100644 --- a/chrome/browser/android/signin/signin_manager_android.h +++ b/chrome/browser/android/signin/signin_manager_android.h @@ -42,12 +42,7 @@ class SigninManagerAndroid { // Indicates that the user has made the choice to sign-in. |username| // contains the email address of the account to use as primary. - // |accountIds| and |accoundNames| are two arrays of equal length: one - // containing strings of stable account ids and the other containing - // strings of account names (or emails). An account id corresponds - // with the account name at the same position in the array. - void OnSignInCompleted(JNIEnv* env, jobject obj, jstring username, - jobjectArray accountIds, jobjectArray accountNames); + void OnSignInCompleted(JNIEnv* env, jobject obj, jstring username); void SignOut(JNIEnv* env, jobject obj); diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc b/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc index a73af22a7e19..ae35fd9dec22 100644 --- a/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc +++ b/chrome/browser/policy/cloud/user_policy_signin_service_mobile.cc @@ -69,8 +69,9 @@ UserPolicySigninService::~UserPolicySigninService() {} void UserPolicySigninService::RegisterForPolicy( const std::string& username, + const std::string& account_id, const PolicyRegistrationCallback& callback) { - RegisterForPolicyInternal(username, "", callback); + RegisterForPolicyInternal(username, account_id, "", callback); } #if !defined(OS_ANDROID) @@ -78,7 +79,7 @@ void UserPolicySigninService::RegisterForPolicyWithAccessToken( const std::string& username, const std::string& access_token, const PolicyRegistrationCallback& callback) { - RegisterForPolicyInternal(username, access_token, callback); + RegisterForPolicyInternal(username, "", access_token, callback); } // static @@ -89,6 +90,7 @@ std::vector UserPolicySigninService::GetScopes() { void UserPolicySigninService::RegisterForPolicyInternal( const std::string& username, + const std::string& account_id, const std::string& access_token, const PolicyRegistrationCallback& callback) { // Create a new CloudPolicyClient for fetching the DMToken. @@ -109,11 +111,9 @@ void UserPolicySigninService::RegisterForPolicyInternal( if (access_token.empty()) { registration_helper_->StartRegistration( - oauth2_token_service_, - username, + oauth2_token_service_, account_id, base::Bind(&UserPolicySigninService::CallPolicyRegistrationCallback, - base::Unretained(this), - base::Passed(&policy_client), + base::Unretained(this), base::Passed(&policy_client), callback)); } else { #if defined(OS_ANDROID) diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_mobile.h b/chrome/browser/policy/cloud/user_policy_signin_service_mobile.h index 96592864853c..56d6958ab8c1 100644 --- a/chrome/browser/policy/cloud/user_policy_signin_service_mobile.h +++ b/chrome/browser/policy/cloud/user_policy_signin_service_mobile.h @@ -44,9 +44,12 @@ class UserPolicySigninService : public UserPolicySigninServiceBase { // Registers a CloudPolicyClient for fetching policy for |username|. // This requests an OAuth2 token for the services involved, and contacts // the policy service if the account has management enabled. + // |account_id| is the obfuscated identitifcation of |username| to get OAuth2 + // token services. // |callback| is invoked once we have registered this device to fetch policy, // or once it is determined that |username| is not a managed account. void RegisterForPolicy(const std::string& username, + const std::string& account_id, const PolicyRegistrationCallback& callback); #if !defined(OS_ANDROID) @@ -65,10 +68,10 @@ class UserPolicySigninService : public UserPolicySigninServiceBase { #endif private: - void RegisterForPolicyInternal( - const std::string& username, - const std::string& access_token, - const PolicyRegistrationCallback& callback); + void RegisterForPolicyInternal(const std::string& username, + const std::string& account_id, + const std::string& access_token, + const PolicyRegistrationCallback& callback); void CallPolicyRegistrationCallback(scoped_ptr client, PolicyRegistrationCallback callback); diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc index f40de8c9ab7d..6473b1e4ff7f 100644 --- a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc +++ b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc @@ -127,7 +127,9 @@ class UserPolicySigninServiceTest : public testing::Test { #endif service->RegisterForPolicy( kTestUser, -#if !defined(OS_ANDROID) +#if defined(OS_ANDROID) + kTestGaiaId, +#else "mock_oauth_token", #endif base::Bind(&UserPolicySigninServiceTest::OnRegisterCompleted, diff --git a/chrome/browser/signin/oauth2_token_service_delegate_android.cc b/chrome/browser/signin/oauth2_token_service_delegate_android.cc index 58d6f6e0af12..aceb33949d94 100644 --- a/chrome/browser/signin/oauth2_token_service_delegate_android.cc +++ b/chrome/browser/signin/oauth2_token_service_delegate_android.cc @@ -12,6 +12,7 @@ #include "chrome/browser/profiles/profile_android.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/sync/profile_sync_service_android.h" +#include "components/signin/core/browser/account_info.h" #include "content/public/browser/browser_thread.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/oauth2_access_token_fetcher.h" @@ -138,12 +139,40 @@ OAuth2TokenServiceDelegateAndroid::ErrorInfo::ErrorInfo( const GoogleServiceAuthError& error) : error(error) {} -OAuth2TokenServiceDelegateAndroid::OAuth2TokenServiceDelegateAndroid() { +OAuth2TokenServiceDelegateAndroid::OAuth2TokenServiceDelegateAndroid( + AccountTrackerService* account_tracker_service) + : account_tracker_service_(account_tracker_service), + fire_refresh_token_loaded_(RT_LOAD_NOT_START) { DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ctor"; + DCHECK(account_tracker_service_); JNIEnv* env = AttachCurrentThread(); base::android::ScopedJavaLocalRef local_java_ref = - Java_OAuth2TokenService_create(env, reinterpret_cast(this)); + Java_OAuth2TokenService_create(env, + base::android::GetApplicationContext(), + reinterpret_cast(this)); java_ref_.Reset(env, local_java_ref.obj()); + + if (account_tracker_service_->GetMigrationState() == + AccountTrackerService::MIGRATION_IN_PROGRESS) { + std::vector accounts = GetAccounts(); + std::vector accounts_id; + for (auto account_name : accounts) { + AccountInfo account_info = + account_tracker_service_->FindAccountInfoByEmail(account_name); + DCHECK(!account_info.gaia.empty()); + accounts_id.push_back(account_info.gaia); + } + ScopedJavaLocalRef java_accounts( + base::android::ToJavaArrayOfStrings(env, accounts_id)); + Java_OAuth2TokenService_saveStoredAccounts( + env, base::android::GetApplicationContext(), java_accounts.obj()); + } + + if (!is_testing_profile_) { + Java_OAuth2TokenService_validateAccounts( + AttachCurrentThread(), java_ref_.obj(), + base::android::GetApplicationContext(), JNI_TRUE); + } } OAuth2TokenServiceDelegateAndroid::~OAuth2TokenServiceDelegateAndroid() { @@ -170,20 +199,14 @@ static ScopedJavaLocalRef GetForProfile( j_profile_android); } -void OAuth2TokenServiceDelegateAndroid::Initialize() { - DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::Initialize"; - if (!is_testing_profile_) { - Java_OAuth2TokenService_validateAccounts( - AttachCurrentThread(), java_ref_.obj(), - base::android::GetApplicationContext(), JNI_TRUE); - } -} - bool OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable( const std::string& account_id) const { + DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable" + << " account= " << account_id; + std::string account_name = MapAccountIdToAccountName(account_id); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef j_account_id = - ConvertUTF8ToJavaString(env, account_id); + ConvertUTF8ToJavaString(env, account_name); jboolean refresh_token_is_available = Java_OAuth2TokenService_hasOAuth2RefreshToken( env, base::android::GetApplicationContext(), j_account_id.obj()); @@ -224,15 +247,15 @@ std::vector OAuth2TokenServiceDelegateAndroid::GetAccounts() { } std::vector -OAuth2TokenServiceDelegateAndroid::GetSystemAccounts() { - std::vector accounts; +OAuth2TokenServiceDelegateAndroid::GetSystemAccountNames() { + std::vector account_names; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef j_accounts = - Java_OAuth2TokenService_getSystemAccounts( + Java_OAuth2TokenService_getSystemAccountNames( env, base::android::GetApplicationContext()); base::android::AppendJavaStringArrayToStringVector(env, j_accounts.obj(), - &accounts); - return accounts; + &account_names); + return account_names; } OAuth2AccessTokenFetcher* @@ -240,8 +263,11 @@ OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher( const std::string& account_id, net::URLRequestContextGetter* getter, OAuth2AccessTokenConsumer* consumer) { + DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher" + << " account= " << account_id; ValidateAccountId(account_id); - return new AndroidAccessTokenFetcher(consumer, account_id); + return new AndroidAccessTokenFetcher(consumer, + MapAccountIdToAccountName(account_id)); } void OAuth2TokenServiceDelegateAndroid::InvalidateAccessToken( @@ -272,20 +298,22 @@ void OAuth2TokenServiceDelegateAndroid::ValidateAccounts( // Clear any auth errors so that client can retry to get access tokens. errors_.clear(); - ValidateAccounts(signed_in_account, j_force_notifications != JNI_FALSE); + ValidateAccounts(MapAccountNameToAccountId(signed_in_account), + j_force_notifications != JNI_FALSE); } void OAuth2TokenServiceDelegateAndroid::ValidateAccounts( const std::string& signed_in_account, bool force_notifications) { std::vector prev_ids = GetAccounts(); - std::vector curr_ids = GetSystemAccounts(); + std::vector curr_ids = GetSystemAccountNames(); std::vector refreshed_ids; std::vector revoked_ids; + bool account_validation_result = true; - // Canonicalize system accounts. |prev_ids| is already done. for (size_t i = 0; i < curr_ids.size(); ++i) - curr_ids[i] = gaia::CanonicalizeEmail(curr_ids[i]); + curr_ids[i] = MapAccountNameToAccountId(curr_ids[i]); + for (size_t i = 0; i < prev_ids.size(); ++i) ValidateAccountId(prev_ids[i]); @@ -294,16 +322,20 @@ void OAuth2TokenServiceDelegateAndroid::ValidateAccounts( << " prev_ids=" << prev_ids.size() << " curr_ids=" << curr_ids.size() << " force=" << (force_notifications ? "true" : "false"); - if (!ValidateAccounts(signed_in_account, prev_ids, curr_ids, refreshed_ids, - revoked_ids, force_notifications)) { - curr_ids.clear(); - } + account_validation_result = + ValidateAccounts(signed_in_account, prev_ids, curr_ids, refreshed_ids, + revoked_ids, force_notifications); ScopedBatchChange batch(this); JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef java_accounts( - base::android::ToJavaArrayOfStrings(env, curr_ids)); + ScopedJavaLocalRef java_accounts; + if (account_validation_result) { + java_accounts = base::android::ToJavaArrayOfStrings(env, curr_ids); + } else { + java_accounts = + base::android::ToJavaArrayOfStrings(env, std::vector()); + } Java_OAuth2TokenService_saveStoredAccounts( env, base::android::GetApplicationContext(), java_accounts.obj()); @@ -316,6 +348,26 @@ void OAuth2TokenServiceDelegateAndroid::ValidateAccounts( it != revoked_ids.end(); it++) { FireRefreshTokenRevoked(*it); } + + if (fire_refresh_token_loaded_ == RT_WAIT_FOR_VALIDATION) { + fire_refresh_token_loaded_ = RT_LOADED; + FireRefreshTokensLoaded(); + } else if (fire_refresh_token_loaded_ == RT_LOAD_NOT_START) { + fire_refresh_token_loaded_ = RT_HAS_BEEN_VALIDATED; + } + + // Clear accounts no longer exist on device from AccountTrackerService. + std::vector accounts_info = + account_tracker_service_->GetAccounts(); + for (auto info : accounts_info) { + auto it = curr_ids.begin(); + for (; it != curr_ids.end(); ++it) { + if (*it == info.account_id) + break; + } + if (it == curr_ids.end()) + account_tracker_service_->RemoveAccount(info.account_id); + } } bool OAuth2TokenServiceDelegateAndroid::ValidateAccounts( @@ -389,7 +441,8 @@ void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailableFromJava( JNIEnv* env, jobject obj, const jstring account_name) { - std::string account_id = ConvertJavaStringToUTF8(env, account_name); + std::string account_id = + MapAccountNameToAccountId(ConvertJavaStringToUTF8(env, account_name)); // Notify native observers. FireRefreshTokenAvailable(account_id); } @@ -400,7 +453,7 @@ void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable( << account_id; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef account_name = - ConvertUTF8ToJavaString(env, account_id); + ConvertUTF8ToJavaString(env, MapAccountIdToAccountName(account_id)); Java_OAuth2TokenService_notifyRefreshTokenAvailable(env, java_ref_.obj(), account_name.obj()); OAuth2TokenServiceDelegate::FireRefreshTokenAvailable(account_id); @@ -410,7 +463,8 @@ void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevokedFromJava( JNIEnv* env, jobject obj, const jstring account_name) { - std::string account_id = ConvertJavaStringToUTF8(env, account_name); + std::string account_id = + MapAccountNameToAccountId(ConvertJavaStringToUTF8(env, account_name)); // Notify native observers. FireRefreshTokenRevoked(account_id); } @@ -421,7 +475,7 @@ void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked( << account_id; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef account_name = - ConvertUTF8ToJavaString(env, account_id); + ConvertUTF8ToJavaString(env, MapAccountIdToAccountName(account_id)); Java_OAuth2TokenService_notifyRefreshTokenRevoked(env, java_ref_.obj(), account_name.obj()); OAuth2TokenServiceDelegate::FireRefreshTokenRevoked(account_id); @@ -459,6 +513,32 @@ void OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() { env, base::android::GetApplicationContext(), java_accounts.obj()); } +void OAuth2TokenServiceDelegateAndroid::LoadCredentials( + const std::string& primary_account_id) { + if (fire_refresh_token_loaded_ == RT_HAS_BEEN_VALIDATED) { + fire_refresh_token_loaded_ = RT_LOADED; + FireRefreshTokensLoaded(); + } else if (fire_refresh_token_loaded_ == RT_LOAD_NOT_START) { + fire_refresh_token_loaded_ = RT_WAIT_FOR_VALIDATION; + } +} + +std::string OAuth2TokenServiceDelegateAndroid::MapAccountIdToAccountName( + const std::string& account_id) const { + std::string account_name = + account_tracker_service_->GetAccountInfo(account_id).email; + DCHECK(!account_name.empty() || account_id.empty()); + return account_name; +} + +std::string OAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId( + const std::string& account_name) const { + std::string account_id = + account_tracker_service_->FindAccountInfoByEmail(account_name).account_id; + DCHECK(!account_id.empty() || account_name.empty()); + return account_id; +} + // Called from Java when fetching of an OAuth2 token is finished. The // |authToken| param is only valid when |result| is true. void OAuth2TokenFetched(JNIEnv* env, diff --git a/chrome/browser/signin/oauth2_token_service_delegate_android.h b/chrome/browser/signin/oauth2_token_service_delegate_android.h index a35837d22c32..b398f8447492 100644 --- a/chrome/browser/signin/oauth2_token_service_delegate_android.h +++ b/chrome/browser/signin/oauth2_token_service_delegate_android.h @@ -14,6 +14,7 @@ #include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" +#include "components/signin/core/browser/account_tracker_service.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_token_service_delegate.h" @@ -44,8 +45,6 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { // accounts which requires special permission. static void set_is_testing_profile() { is_testing_profile_ = true; } - void Initialize(); - // OAuth2TokenServiceDelegate overrides: bool RefreshTokenIsAvailable(const std::string& account_id) const override; bool RefreshTokenHasError(const std::string& account_id) const override; @@ -53,8 +52,8 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { const GoogleServiceAuthError& error) override; std::vector GetAccounts() override; - // Lists account at the OS level. - std::vector GetSystemAccounts(); + // Lists account names at the OS level. + std::vector GetSystemAccountNames(); void ValidateAccounts(JNIEnv* env, jobject obj, @@ -87,9 +86,12 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { // OA2TService aware accounts. void RevokeAllCredentials() override; + void LoadCredentials(const std::string& primary_account_id) override; + protected: friend class ProfileOAuth2TokenServiceFactory; - OAuth2TokenServiceDelegateAndroid(); + OAuth2TokenServiceDelegateAndroid( + AccountTrackerService* account_tracker_service); ~OAuth2TokenServiceDelegateAndroid() override; OAuth2AccessTokenFetcher* CreateAccessTokenFetcher( @@ -112,12 +114,22 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { void FireRefreshTokensLoaded() override; private: + std::string MapAccountIdToAccountName(const std::string& account_id) const; + std::string MapAccountNameToAccountId(const std::string& account_name) const; + struct ErrorInfo { ErrorInfo(); explicit ErrorInfo(const GoogleServiceAuthError& error); GoogleServiceAuthError error; }; + enum RefreshTokenLoadStatus { + RT_LOAD_NOT_START, + RT_WAIT_FOR_VALIDATION, + RT_HAS_BEEN_VALIDATED, + RT_LOADED + }; + // Return whether |signed_in_account| is valid and we have access // to all the tokens in |curr_account_ids|. If |force_notifications| is true, // TokenAvailable notifications will be sent anyway, even if the account was @@ -134,6 +146,9 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { // Maps account_id to the last error for that account. std::map errors_; + AccountTrackerService* account_tracker_service_; + RefreshTokenLoadStatus fire_refresh_token_loaded_; + static bool is_testing_profile_; DISALLOW_COPY_AND_ASSIGN(OAuth2TokenServiceDelegateAndroid); diff --git a/chrome/browser/signin/profile_oauth2_token_service_factory.cc b/chrome/browser/signin/profile_oauth2_token_service_factory.cc index 854c5b473681..a8b65afc0a9e 100644 --- a/chrome/browser/signin/profile_oauth2_token_service_factory.cc +++ b/chrome/browser/signin/profile_oauth2_token_service_factory.cc @@ -49,12 +49,12 @@ ProfileOAuth2TokenServiceFactory* KeyedService* ProfileOAuth2TokenServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { + Profile* profile = static_cast(context); #if defined(OS_ANDROID) OAuth2TokenServiceDelegateAndroid* delegate = - new OAuth2TokenServiceDelegateAndroid(); - delegate->Initialize(); + new OAuth2TokenServiceDelegateAndroid( + AccountTrackerServiceFactory::GetInstance()->GetForProfile(profile)); #else - Profile* profile = static_cast(context); MutableProfileOAuth2TokenServiceDelegate* delegate = new MutableProfileOAuth2TokenServiceDelegate( ChromeSigninClientFactory::GetInstance()->GetForProfile(profile), diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 8447dc7166b8..4dcb6a56c112 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -233,6 +233,8 @@ 'browser/android/shortcut_info.h', 'browser/android/signin/account_management_screen_helper.cc', 'browser/android/signin/account_management_screen_helper.h', + 'browser/android/signin/account_tracker_service_android.cc', + 'browser/android/signin/account_tracker_service_android.h', 'browser/android/signin/signin_manager_android.cc', 'browser/android/signin/signin_manager_android.h', 'browser/android/tab/thumbnail_tab_helper_android.cc', @@ -1807,6 +1809,7 @@ 'android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java', 'android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java', 'android/java/src/org/chromium/chrome/browser/signin/AccountManagementScreenHelper.java', + 'android/java/src/org/chromium/chrome/browser/signin/AccountTrackerService.java', 'android/java/src/org/chromium/chrome/browser/signin/OAuth2TokenService.java', 'android/java/src/org/chromium/chrome/browser/signin/SigninManager.java', 'android/java/src/org/chromium/chrome/browser/snackbar/smartlockautosignin/AutoSigninSnackbarController.java', diff --git a/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc b/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc index 47b7350f1dfb..235af5832079 100644 --- a/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc +++ b/components/policy/core/common/cloud/cloud_policy_client_registration_helper.cc @@ -170,7 +170,7 @@ void CloudPolicyClientRegistrationHelper::StartRegistration( OAuth2TokenService* token_service, const std::string& account_id, const base::Closure& callback) { - DVLOG(1) << "Starting registration process with username"; + DVLOG(1) << "Starting registration process with account_id"; DCHECK(!client_->is_registered()); callback_ = callback; client_->AddObserver(this); diff --git a/components/signin/core/browser/account_tracker_service.cc b/components/signin/core/browser/account_tracker_service.cc index 5c889241054d..59250336be5f 100644 --- a/components/signin/core/browser/account_tracker_service.cc +++ b/components/signin/core/browser/account_tracker_service.cc @@ -254,7 +254,7 @@ void AccountTrackerService::SetIsChildAccount(const std::string& account_id, } bool AccountTrackerService::IsMigratable() { -#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) +#if !defined(OS_CHROMEOS) for (std::map::const_iterator it = accounts_.begin(); it != accounts_.end(); ++it) { @@ -501,3 +501,7 @@ void AccountTrackerService::SeedAccountInfo(AccountInfo info) { SaveToPrefs(state); } } + +void AccountTrackerService::RemoveAccount(const std::string& account_id) { + StopTrackingAccount(account_id); +} diff --git a/components/signin/core/browser/account_tracker_service.h b/components/signin/core/browser/account_tracker_service.h index 0c3f85d40f7b..2a05ed2fff4f 100644 --- a/components/signin/core/browser/account_tracker_service.h +++ b/components/signin/core/browser/account_tracker_service.h @@ -108,6 +108,8 @@ class AccountTrackerService : public KeyedService, const std::string& email); void SeedAccountInfo(AccountInfo info); + void RemoveAccount(const std::string& account_id); + AccountIdMigrationState GetMigrationState(); void SetMigrationDone(); static AccountIdMigrationState GetMigrationState(PrefService* pref_service); -- 2.11.4.GIT