diff options
16 files changed, 873 insertions, 164 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/profiles/Profile.java b/chrome/android/java/src/org/chromium/chrome/browser/profiles/Profile.java index 4cec13e..138ebbc 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/profiles/Profile.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/profiles/Profile.java @@ -17,6 +17,10 @@ public class Profile { mNativeProfileAndroid = nativeProfileAndroid; } + public static Profile getLastUsedProfile() { + return (Profile) nativeGetLastUsedProfile(); + } + @CalledByNative private static Profile create(int nativeProfileAndroid) { return new Profile(nativeProfileAndroid); @@ -31,4 +35,6 @@ public class Profile { private int getNativePointer() { return mNativeProfileAndroid; } + + private static native Object nativeGetLastUsedProfile(); } diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelper.java index 09d73a1..5a47988 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelper.java @@ -7,163 +7,46 @@ package org.chromium.chrome.browser.signin; import android.accounts.Account; import android.app.Activity; import android.content.Context; -import android.util.Log; -import org.chromium.base.CalledByNative; -import org.chromium.base.ThreadUtils; import org.chromium.sync.signin.AccountManagerHelper; -import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; /** - * Helper class for working with access tokens from native code. - * <p/> - * This class forwards calls to request or invalidate access tokens made by native code to - * AccountManagerHelper and forwards callbacks to native code. - * <p/> + * Temporary wrapper class until all callers have moved to use {@link OAuth2TokenService}. + * TODO(nyquist) Remove this class. */ public final class AndroidProfileOAuth2TokenServiceHelper { - private static final String TAG = "AndroidProfileOAuth2TokenServiceHelper"; - - private static final String OAUTH2_SCOPE_PREFIX = "oauth2:"; - - private AndroidProfileOAuth2TokenServiceHelper() { - } - - private static Account getAccountOrNullFromUsername(Context context, String username) { - if (username == null) { - Log.e(TAG, "Username is null"); - return null; - } - - AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context); - Account account = accountManagerHelper.getAccountFromName(username); - if (account == null) { - Log.e(TAG, "Account not found for provided username."); - return null; - } - return account; - } - - /** - * Called by native to list the accounts with OAuth2 refresh tokens. - */ - @CalledByNative - public static String[] getAccounts(Context context) { - AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context); - java.util.List<String> accountNames = accountManagerHelper.getGoogleAccountNames(); - return accountNames.toArray(new String[accountNames.size()]); - } - - /** - * Called by native to retrieve OAuth2 tokens. - * - * @param username The native username (full address). - * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix). - * @param nativeCallback The pointer to the native callback that should be run upon completion. - */ - @CalledByNative - public static void getOAuth2AuthToken( - Context context, String username, String scope, final int nativeCallback) { - Account account = getAccountOrNullFromUsername(context, username); - if (account == null) { - nativeOAuth2TokenFetched(null, false, nativeCallback); - return; - } - String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope; - - AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context); - accountManagerHelper.getAuthTokenFromForeground( - null, account, oauth2Scope, new AccountManagerHelper.GetAuthTokenCallback() { - @Override - public void tokenAvailable(String token) { - nativeOAuth2TokenFetched( - token, token != null, nativeCallback); - } - }); - } + private AndroidProfileOAuth2TokenServiceHelper() {} /** - * Call this method to retrieve an OAuth2 access token for the given account and scope. - * - * @param activity the current activity. May be null. - * @param account the account to get the access token for. - * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix). - * @param callback called on successful and unsuccessful fetching of auth token. + * Use {@link OAuth2TokenService#getOAuth2AccessToken} instead. */ + @Deprecated public static void getOAuth2AccessToken(Context context, @Nullable Activity activity, - Account account, String scope, - AccountManagerHelper.GetAuthTokenCallback callback) { - String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope; - AccountManagerHelper.get(context).getAuthTokenFromForeground( - activity, account, oauth2Scope, callback); + Account account, String scope, AccountManagerHelper.GetAuthTokenCallback callback) { + OAuth2TokenService.getOAuth2AccessToken(context, activity, account, scope, callback); } /** - * Call this method to retrieve an OAuth2 access token for the given account and scope. This - * method times out after the specified timeout, and will return null if that happens. - * - * Given that this is a blocking method call, this should never be called from the UI thread. - * - * @param activity the current activity. May be null. - * @param account the account to get the access token for. - * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix). - * @param timeout the timeout. - * @param unit the unit for |timeout|. + * Use {@link OAuth2TokenService#invalidateOAuth2AuthToken} instead. */ - public static String getOAuth2AccessTokenWithTimeout( - Context context, @Nullable Activity activity, Account account, String scope, - long timeout, TimeUnit unit) { - assert !ThreadUtils.runningOnUiThread(); - final AtomicReference<String> result = new AtomicReference<String>(); - final Semaphore semaphore = new Semaphore(0); - getOAuth2AccessToken( - context, activity, account, scope, - new AccountManagerHelper.GetAuthTokenCallback() { - @Override - public void tokenAvailable(String token) { - result.set(token); - semaphore.release(); - } - }); - try { - if (semaphore.tryAcquire(timeout, unit)) { - return result.get(); - } else { - Log.d(TAG, "Failed to retrieve auth token within timeout (" + - timeout + " + " + unit.name() + ")"); - return null; - } - } catch (InterruptedException e) { - Log.w(TAG, "Got interrupted while waiting for auth token"); - return null; - } + @Deprecated + public static void invalidateOAuth2AuthToken(Context context, String accessToken) { + OAuth2TokenService.invalidateOAuth2AuthToken(context, accessToken); } /** - * Called by native to check wether the account has an OAuth2 refresh token. + * Use {@link OAuth2TokenService#getOAuth2AccessTokenWithTimeout} instead. */ - @CalledByNative - public static boolean hasOAuth2RefreshToken(Context context, String accountName) { - return AccountManagerHelper.get(context).hasAccountForName(accountName); - } - - /** - * Called by native to invalidate an OAuth2 token. - */ - @CalledByNative - public static void invalidateOAuth2AuthToken(Context context, String accessToken) { - if (accessToken != null) { - AccountManagerHelper.get(context).invalidateAuthToken(accessToken); - } + @Deprecated + public static String getOAuth2AccessTokenWithTimeout(Context context, + @Nullable Activity activity, Account account, String scope, + long timeout, TimeUnit unit) { + return OAuth2TokenService.getOAuth2AccessTokenWithTimeout( + context, activity, account, scope, timeout, unit); } - - private static native void nativeOAuth2TokenFetched( - String authToken, boolean result, int nativeCallback); - } 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 new file mode 100644 index 0000000..6f50e49 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/OAuth2TokenService.java @@ -0,0 +1,276 @@ +// 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.signin; + +import android.accounts.Account; +import android.app.Activity; +import android.content.Context; +import android.util.Log; + +import org.chromium.base.CalledByNative; +import org.chromium.base.ObserverList; +import org.chromium.base.ThreadUtils; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.sync.signin.AccountManagerHelper; +import org.chromium.sync.signin.ChromeSigninController; + +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import javax.annotation.Nullable; + +/** + * Java instance for the native OAuth2TokenService. + * <p/> + * This class forwards calls to request or invalidate access tokens made by native code to + * AccountManagerHelper and forwards callbacks to native code. + * <p/> + */ +public final class OAuth2TokenService { + + private static final String TAG = "OAuth2TokenService"; + + public interface OAuth2TokenServiceObserver { + void onRefreshTokenAvailable(Account account); + void onRefreshTokenRevoked(Account account); + void onRefreshTokensLoaded(); + } + + private static final String OAUTH2_SCOPE_PREFIX = "oauth2:"; + + private final int mNativeProfileOAuth2TokenService; + private final ObserverList<OAuth2TokenServiceObserver> mObservers; + + private OAuth2TokenService(int nativeOAuth2Service) { + mNativeProfileOAuth2TokenService = nativeOAuth2Service; + mObservers = new ObserverList<OAuth2TokenServiceObserver>(); + } + + public static OAuth2TokenService getForProfile(Profile profile) { + ThreadUtils.assertOnUiThread(); + return (OAuth2TokenService) nativeGetForProfile(profile); + } + + @CalledByNative + private static OAuth2TokenService create(int nativeOAuth2Service) { + ThreadUtils.assertOnUiThread(); + return new OAuth2TokenService(nativeOAuth2Service); + } + + public void addObserver(OAuth2TokenServiceObserver observer) { + ThreadUtils.assertOnUiThread(); + mObservers.addObserver(observer); + } + + public void removeObserver(OAuth2TokenServiceObserver observer) { + ThreadUtils.assertOnUiThread(); + mObservers.removeObserver(observer); + } + + private static Account getAccountOrNullFromUsername(Context context, String username) { + if (username == null) { + Log.e(TAG, "Username is null"); + return null; + } + + AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context); + Account account = accountManagerHelper.getAccountFromName(username); + if (account == null) { + Log.e(TAG, "Account not found for provided username."); + return null; + } + return account; + } + + /** + * Called by native to list the accounts with OAuth2 refresh tokens. + */ + @CalledByNative + public static String[] getAccounts(Context context) { + AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context); + java.util.List<String> accountNames = accountManagerHelper.getGoogleAccountNames(); + return accountNames.toArray(new String[accountNames.size()]); + } + + /** + * Called by native to retrieve OAuth2 tokens. + * + * @param username The native username (full address). + * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix). + * @param nativeCallback The pointer to the native callback that should be run upon completion. + */ + @CalledByNative + public static void getOAuth2AuthToken( + Context context, String username, String scope, final int nativeCallback) { + Account account = getAccountOrNullFromUsername(context, username); + if (account == null) { + nativeOAuth2TokenFetched(null, false, nativeCallback); + return; + } + String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope; + + AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context); + accountManagerHelper.getAuthTokenFromForeground( + null, account, oauth2Scope, new AccountManagerHelper.GetAuthTokenCallback() { + @Override + public void tokenAvailable(String token) { + nativeOAuth2TokenFetched( + token, token != null, nativeCallback); + } + }); + } + + /** + * Call this method to retrieve an OAuth2 access token for the given account and scope. + * + * @param activity the current activity. May be null. + * @param account the account to get the access token for. + * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix). + * @param callback called on successful and unsuccessful fetching of auth token. + */ + public static void getOAuth2AccessToken(Context context, @Nullable Activity activity, + Account account, String scope, + AccountManagerHelper.GetAuthTokenCallback callback) { + String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope; + AccountManagerHelper.get(context).getAuthTokenFromForeground( + activity, account, oauth2Scope, callback); + } + + /** + * Call this method to retrieve an OAuth2 access token for the given account and scope. This + * method times out after the specified timeout, and will return null if that happens. + * + * Given that this is a blocking method call, this should never be called from the UI thread. + * + * @param activity the current activity. May be null. + * @param account the account to get the access token for. + * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix). + * @param timeout the timeout. + * @param unit the unit for |timeout|. + */ + public static String getOAuth2AccessTokenWithTimeout( + Context context, @Nullable Activity activity, Account account, String scope, + long timeout, TimeUnit unit) { + assert !ThreadUtils.runningOnUiThread(); + final AtomicReference<String> result = new AtomicReference<String>(); + final Semaphore semaphore = new Semaphore(0); + getOAuth2AccessToken( + context, activity, account, scope, + new AccountManagerHelper.GetAuthTokenCallback() { + @Override + public void tokenAvailable(String token) { + result.set(token); + semaphore.release(); + } + }); + try { + if (semaphore.tryAcquire(timeout, unit)) { + return result.get(); + } else { + Log.d(TAG, "Failed to retrieve auth token within timeout (" + + timeout + " + " + unit.name() + ")"); + return null; + } + } catch (InterruptedException e) { + Log.w(TAG, "Got interrupted while waiting for auth token"); + return null; + } + } + + /** + * Called by native to check wether the account has an OAuth2 refresh token. + */ + @CalledByNative + public static boolean hasOAuth2RefreshToken(Context context, String accountName) { + return AccountManagerHelper.get(context).hasAccountForName(accountName); + } + + /** + * Called by native to invalidate an OAuth2 token. + */ + @CalledByNative + public static void invalidateOAuth2AuthToken(Context context, String accessToken) { + if (accessToken != null) { + AccountManagerHelper.get(context).invalidateAuthToken(accessToken); + } + } + + public void validateAccounts(Context context) { + ThreadUtils.assertOnUiThread(); + String currentlySignedInAccount = + ChromeSigninController.get(context).getSignedInAccountName(); + String[] accounts = getAccounts(context); + nativeValidateAccounts( + mNativeProfileOAuth2TokenService, accounts, currentlySignedInAccount); + } + + /** + * Triggers a notification to all observers of the native and Java instance of the + * OAuth2TokenService that a refresh token is now available. This may cause observers to retry + * operations that require authentication. + */ + public void fireRefreshTokenAvailable(Account account) { + ThreadUtils.assertOnUiThread(); + assert account != null; + nativeFireRefreshTokenAvailableFromJava(mNativeProfileOAuth2TokenService, account.name); + } + + @CalledByNative + public void notifyRefreshTokenAvailable(String accountName) { + assert accountName != null; + Account account = AccountManagerHelper.createAccountFromName(accountName); + for (OAuth2TokenServiceObserver observer : mObservers) { + observer.onRefreshTokenAvailable(account); + } + } + + /** + * Triggers a notification to all observers of the native and Java instance of the + * OAuth2TokenService that a refresh token is now revoked. + */ + public void fireRefreshTokenRevoked(Account account) { + ThreadUtils.assertOnUiThread(); + assert account != null; + nativeFireRefreshTokenRevokedFromJava(mNativeProfileOAuth2TokenService, account.name); + } + + @CalledByNative + public void notifyRefreshTokenRevoked(String accountName) { + assert accountName != null; + Account account = AccountManagerHelper.createAccountFromName(accountName); + for (OAuth2TokenServiceObserver observer : mObservers) { + observer.onRefreshTokenRevoked(account); + } + } + + /** + * Triggers a notification to all observers of the native and Java instance of the + * OAuth2TokenService that all refresh tokens now have been loaded. + */ + public void fireRefreshTokensLoaded() { + ThreadUtils.assertOnUiThread(); + nativeFireRefreshTokensLoadedFromJava(mNativeProfileOAuth2TokenService); + } + + @CalledByNative + public void notifyRefreshTokensLoaded() { + for (OAuth2TokenServiceObserver observer : mObservers) { + observer.onRefreshTokensLoaded(); + } + } + + private static native Object nativeGetForProfile(Profile profile); + private static native void nativeOAuth2TokenFetched( + String authToken, boolean result, int nativeCallback); + private native void nativeValidateAccounts(int nativeAndroidProfileOAuth2TokenService, + String[] accounts, String currentlySignedInAccount); + private native void nativeFireRefreshTokenAvailableFromJava( + int nativeAndroidProfileOAuth2TokenService, String accountName); + private native void nativeFireRefreshTokenRevokedFromJava( + int nativeAndroidProfileOAuth2TokenService, String accountName); + private native void nativeFireRefreshTokensLoadedFromJava( + int nativeAndroidProfileOAuth2TokenService); +} 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 new file mode 100644 index 0000000..c2d2334 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceIntegrationTest.java @@ -0,0 +1,275 @@ +// 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.signin; + +import android.accounts.Account; +import android.test.UiThreadTest; +import android.test.suitebuilder.annotation.MediumTest; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.test.util.AdvancedMockContext; +import org.chromium.base.test.util.Feature; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.testshell.ChromiumTestShellTestBase; +import org.chromium.sync.signin.AccountManagerHelper; +import org.chromium.sync.signin.ChromeSigninController; +import org.chromium.sync.test.util.AccountHolder; +import org.chromium.sync.test.util.MockAccountManager; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * Integration test for the OAuth2TokenService. + * + * These tests initialize the native part of the service. + */ +public class OAuth2TokenServiceIntegrationTest extends ChromiumTestShellTestBase { + + private static final Account TEST_ACCOUNT1 = + AccountManagerHelper.createAccountFromName("foo@gmail.com"); + private static final Account TEST_ACCOUNT2 = + AccountManagerHelper.createAccountFromName("bar@gmail.com"); + private static final AccountHolder TEST_ACCOUNT_HOLDER_1 = + AccountHolder.create().account(TEST_ACCOUNT1).build(); + private static final AccountHolder TEST_ACCOUNT_HOLDER_2 = + AccountHolder.create().account(TEST_ACCOUNT2).build(); + + private AdvancedMockContext mContext; + private OAuth2TokenService mOAuth2TokenService; + private MockAccountManager mAccountManager; + private TestObserver mObserver; + private ChromeSigninController mChromeSigninController; + + @Override + protected void setUp() throws Exception { + super.setUp(); + clearAppData(); + startChromeBrowserProcessSync(getInstrumentation().getTargetContext()); + + // Set up AccountManager. + mContext = new AdvancedMockContext(getInstrumentation().getTargetContext()); + mAccountManager = new MockAccountManager(mContext, getInstrumentation().getContext()); + AccountManagerHelper.overrideAccountManagerHelperForTests(mContext, mAccountManager); + mChromeSigninController = ChromeSigninController.get(mContext); + + // Get a reference to the service. + mOAuth2TokenService = getOAuth2TokenServiceOnUiThread(); + + // Set up observer. + mObserver = new TestObserver(); + addObserver(mObserver); + } + + /** + * 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. + * + * @return the OAuth2TokenService. + */ + private static OAuth2TokenService getOAuth2TokenServiceOnUiThread() { + final AtomicReference<OAuth2TokenService> service = + new AtomicReference<OAuth2TokenService>(); + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + service.set(OAuth2TokenService.getForProfile(Profile.getLastUsedProfile())); + } + }); + return service.get(); + } + + private void addObserver(final TestObserver observer) { + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + mOAuth2TokenService.addObserver(observer); + } + }); + } + + @MediumTest + @UiThreadTest + @Feature({"Sync"}) + public void testFireRefreshTokenAvailableNotifiesJavaObservers() { + // Adding an observer should not lead to a callback. + assertEquals(0, mObserver.getAvailableCallCount()); + + // An observer should be called with the correct account. + mOAuth2TokenService.fireRefreshTokenAvailable(TEST_ACCOUNT1); + assertEquals(1, mObserver.getAvailableCallCount()); + assertEquals(TEST_ACCOUNT1, mObserver.getLastAccount()); + + // When mOAuth2TokenService, an observer should not be called. + mOAuth2TokenService.removeObserver(mObserver); + mOAuth2TokenService.fireRefreshTokenAvailable(TEST_ACCOUNT1); + assertEquals(1, mObserver.getAvailableCallCount()); + + // No other observer interface method should ever have been called. + assertEquals(0, mObserver.getRevokedCallCount()); + assertEquals(0, mObserver.getLoadedCallCount()); + } + + @MediumTest + @UiThreadTest + @Feature({"Sync"}) + public void testFireRefreshTokenRevokedNotifiesJavaObservers() { + // Adding an observer should not lead to a callback. + assertEquals(0, mObserver.getRevokedCallCount()); + + // An observer should be called with the correct account. + mOAuth2TokenService.fireRefreshTokenRevoked(TEST_ACCOUNT1); + assertEquals(1, mObserver.getRevokedCallCount()); + assertEquals(TEST_ACCOUNT1, mObserver.getLastAccount()); + + // When removed, an observer should not be called. + mOAuth2TokenService.removeObserver(mObserver); + mOAuth2TokenService.fireRefreshTokenRevoked(TEST_ACCOUNT1); + assertEquals(1, mObserver.getRevokedCallCount()); + + // No other observer interface method should ever have been called. + assertEquals(0, mObserver.getAvailableCallCount()); + assertEquals(0, mObserver.getLoadedCallCount()); + } + + @MediumTest + @UiThreadTest + @Feature({"Sync"}) + public void testFireRefreshTokensLoadedNotifiesJavaObservers() { + // Adding an observer should not lead to a callback. + assertEquals(0, mObserver.getLoadedCallCount()); + + // An observer should be called with the correct account. + mOAuth2TokenService.fireRefreshTokensLoaded(); + assertEquals(1, mObserver.getLoadedCallCount()); + + // When removed, an observer should not be called. + mOAuth2TokenService.removeObserver(mObserver); + mOAuth2TokenService.fireRefreshTokensLoaded(); + assertEquals(1, mObserver.getLoadedCallCount()); + + // No other observer interface method should ever have been called. + assertEquals(0, mObserver.getAvailableCallCount()); + assertEquals(0, mObserver.getRevokedCallCount()); + } + + @MediumTest + @UiThreadTest + public void testValidateAccountsNoAccountsRegisteredAndNoSignedInUser() { + // Run test. + mOAuth2TokenService.validateAccounts(mContext); + + // Ensure no calls have been made to the observer. + assertEquals(0, mObserver.getAvailableCallCount()); + assertEquals(0, mObserver.getRevokedCallCount()); + assertEquals(0, mObserver.getLoadedCallCount()); + } + + @MediumTest + @UiThreadTest + public void testValidateAccountsOneAccountsRegisteredAndNoSignedInUser() { + // Add account. + mAccountManager.addAccountHolderExplicitly(TEST_ACCOUNT_HOLDER_1); + + // Run test. + mOAuth2TokenService.validateAccounts(mContext); + + // Ensure no calls have been made to the observer. + assertEquals(0, mObserver.getAvailableCallCount()); + assertEquals(0, mObserver.getRevokedCallCount()); + assertEquals(0, mObserver.getLoadedCallCount()); + } + + @MediumTest + @UiThreadTest + public void testValidateAccountsOneAccountsRegisteredSignedIn() { + // Add account. + mAccountManager.addAccountHolderExplicitly(TEST_ACCOUNT_HOLDER_1); + + // Mark user as signed in. + mChromeSigninController.setSignedInAccountName(TEST_ACCOUNT1.name); + + // Run test. + mOAuth2TokenService.validateAccounts(mContext); + + // Ensure no calls have been made to the observer. + assertEquals(1, mObserver.getAvailableCallCount()); + assertEquals(0, mObserver.getRevokedCallCount()); + assertEquals(0, mObserver.getLoadedCallCount()); + } + + @MediumTest + @UiThreadTest + public void testValidateAccountsTwoAccountsRegisteredAndOneSignedIn() { + // Add accounts. + mAccountManager.addAccountHolderExplicitly(TEST_ACCOUNT_HOLDER_1); + mAccountManager.addAccountHolderExplicitly(TEST_ACCOUNT_HOLDER_2); + + // Mark user as signed in. + mChromeSigninController.setSignedInAccountName(TEST_ACCOUNT1.name); + + // Run test. + mOAuth2TokenService.validateAccounts(mContext); + + // Ensure no calls have been made to the observer. + assertEquals(1, mObserver.getAvailableCallCount()); + assertEquals(0, mObserver.getRevokedCallCount()); + assertEquals(0, mObserver.getLoadedCallCount()); + } + + @MediumTest + @UiThreadTest + public void testValidateAccountsNoAccountsRegisteredButSignedIn() { + // Mark user as signed in without setting up the account. + mChromeSigninController.setSignedInAccountName(TEST_ACCOUNT1.name); + + // Run test. + mOAuth2TokenService.validateAccounts(mContext); + + // Ensure no calls have been made to the observer. + assertEquals(0, mObserver.getAvailableCallCount()); + assertEquals(1, mObserver.getRevokedCallCount()); + assertEquals(0, mObserver.getLoadedCallCount()); + } + + private static class TestObserver implements OAuth2TokenService.OAuth2TokenServiceObserver { + private int mAvailableCallCount; + private int mRevokedCallCount; + private int mLoadedCallCount; + private Account mLastAccount; + + @Override + public void onRefreshTokenAvailable(Account account) { + mAvailableCallCount++; + mLastAccount = account; + } + + @Override + public void onRefreshTokenRevoked(Account account) { + mRevokedCallCount++; + mLastAccount = account; + } + + @Override + public void onRefreshTokensLoaded() { + mLoadedCallCount++; + } + + public int getAvailableCallCount() { + return mAvailableCallCount; + } + + public int getRevokedCallCount() { + return mRevokedCallCount; + } + + public int getLoadedCallCount() { + return mLoadedCallCount; + } + + public Account getLastAccount() { + return mLastAccount; + } + } +} diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java index ef9c5c9..78baa84 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AndroidProfileOAuth2TokenServiceHelperTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/OAuth2TokenServiceTest.java @@ -14,9 +14,10 @@ import org.chromium.sync.signin.AccountManagerHelper; import org.chromium.sync.test.util.AccountHolder; import org.chromium.sync.test.util.MockAccountManager; +import java.util.Arrays; import java.util.concurrent.TimeUnit; -public class AndroidProfileOAuth2TokenServiceHelperTest extends InstrumentationTestCase { +public class OAuth2TokenServiceTest extends InstrumentationTestCase { private AdvancedMockContext mContext; private MockAccountManager mAccountManager; @@ -33,6 +34,43 @@ public class AndroidProfileOAuth2TokenServiceHelperTest extends InstrumentationT @SmallTest @Feature({"Sync"}) + public void testGetAccountsNoAccountsRegistered() { + String[] accounts = OAuth2TokenService.getAccounts(mContext); + assertEquals("There should be no accounts registered", 0, accounts.length); + } + + @SmallTest + @Feature({"Sync"}) + public void testGetAccountsOneAccountRegistered() { + Account account1 = AccountManagerHelper.createAccountFromName("foo@gmail.com"); + AccountHolder accountHolder1 = AccountHolder.create().account(account1).build(); + mAccountManager.addAccountHolderExplicitly(accountHolder1); + + String[] accounts = OAuth2TokenService.getAccounts(mContext); + assertEquals("There should be one registered account", 1, accounts.length); + assertEquals("The account should be " + account1, account1.name, accounts[0]); + } + + @SmallTest + @Feature({"Sync"}) + public void testGetAccountsTwoAccountsRegistered() { + Account account1 = AccountManagerHelper.createAccountFromName("foo@gmail.com"); + AccountHolder accountHolder1 = AccountHolder.create().account(account1).build(); + mAccountManager.addAccountHolderExplicitly(accountHolder1); + Account account2 = AccountManagerHelper.createAccountFromName("bar@gmail.com"); + AccountHolder accountHolder2 = AccountHolder.create().account(account2).build(); + mAccountManager.addAccountHolderExplicitly(accountHolder2); + + String[] accounts = OAuth2TokenService.getAccounts(mContext); + assertEquals("There should be one registered account", 2, accounts.length); + assertTrue("The list should contain " + account1, + Arrays.asList(accounts).contains(account1.name)); + assertTrue("The list should contain " + account2, + Arrays.asList(accounts).contains(account2.name)); + } + + @SmallTest + @Feature({"Sync"}) public void testGetOAuth2AccessTokenWithTimeoutOnSuccess() { String authToken = "someToken"; // Auth token should be successfully received. @@ -61,7 +99,7 @@ public class AndroidProfileOAuth2TokenServiceHelperTest extends InstrumentationT mAccountManager.addAccountHolderExplicitly(accountHolder); String accessToken = - AndroidProfileOAuth2TokenServiceHelper.getOAuth2AccessTokenWithTimeout( + OAuth2TokenService.getOAuth2AccessTokenWithTimeout( mContext, null, account, scope, 5, TimeUnit.SECONDS); assertEquals(expectedToken, accessToken); } diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java index f5fa2fa..b8ccc47 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java @@ -6,7 +6,6 @@ package org.chromium.chrome.browser.sync; import android.accounts.Account; import android.app.Activity; -import android.content.Context; import android.util.Log; import org.chromium.base.ThreadUtils; @@ -18,14 +17,12 @@ import org.chromium.chrome.test.util.browser.sync.SyncTestUtil; import org.chromium.chrome.testshell.ChromiumTestShellActivity; import org.chromium.chrome.testshell.ChromiumTestShellTestBase; import org.chromium.chrome.testshell.sync.SyncController; -import org.chromium.content.browser.BrowserStartupController; import org.chromium.content.browser.ContentView; import org.chromium.content.browser.ContentViewCore; import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.JavaScriptUtils; import org.chromium.content.browser.test.util.TestCallbackHelperContainer; -import org.chromium.content.common.CommandLine; import org.chromium.sync.notifier.SyncStatusHelper; import org.chromium.sync.signin.AccountManagerHelper; import org.chromium.sync.signin.ChromeSigninController; @@ -75,17 +72,6 @@ public class SyncTest extends ChromiumTestShellTestBase { SyncTestUtil.verifySyncServerIsRunning(); } - private static void startChromeBrowserProcessSync(final Context targetContext) { - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - CommandLine.initFromFile("/data/local/tmp/chromium-testshell-command-line"); - BrowserStartupController.get(targetContext).startBrowserProcessesSync( - BrowserStartupController.MAX_RENDERERS_LIMIT); - } - }); - } - @HostDrivenTest public void testGetAboutSyncInfoYieldsValidData() throws Throwable { setupTestAccountAndSignInToSync(FOREIGN_SESSION_TEST_MACHINE_ID); diff --git a/chrome/android/testshell/java/AndroidManifest.xml b/chrome/android/testshell/java/AndroidManifest.xml index 71f6b00..1628bc3 100644 --- a/chrome/android/testshell/java/AndroidManifest.xml +++ b/chrome/android/testshell/java/AndroidManifest.xml @@ -180,5 +180,12 @@ <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" /> </service> + + <!-- Broadcast receiver that will be notified of account changes. --> + <receiver android:name="org.chromium.chrome.testshell.signin.AccountsChangedReceiver"> + <intent-filter> + <action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" /> + </intent-filter> + </receiver> </application> </manifest> diff --git a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/signin/AccountsChangedReceiver.java b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/signin/AccountsChangedReceiver.java new file mode 100644 index 0000000..dc97f30 --- /dev/null +++ b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/signin/AccountsChangedReceiver.java @@ -0,0 +1,59 @@ +// 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.testshell.signin; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import org.chromium.base.ThreadUtils; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.signin.OAuth2TokenService; +import org.chromium.content.browser.BrowserStartupController; +import org.chromium.sync.signin.ChromeSigninController; + +/** + * A BroadcastReceiver for acting on changes to Android accounts. + */ +public class AccountsChangedReceiver extends BroadcastReceiver { + private static final String TAG = "AccountsChangedReceiver"; + + @Override + public void onReceive(final Context context, Intent intent) { + if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(intent.getAction())) { + final Account signedInUser = + ChromeSigninController.get(context).getSignedInUser(); + if (signedInUser != null) { + BrowserStartupController.StartupCallback callback = + new BrowserStartupController.StartupCallback() { + @Override + public void onSuccess(boolean alreadyStarted) { + OAuth2TokenService.getForProfile(Profile.getLastUsedProfile()) + .validateAccounts(context); + } + + @Override + public void onFailure() { + Log.w(TAG, "Failed to start browser process."); + } + }; + startBrowserProcessOnUiThread(context, callback); + } + } + } + + private static void startBrowserProcessOnUiThread(final Context context, + final BrowserStartupController.StartupCallback callback) { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + BrowserStartupController.get(context).startBrowserProcessesAsync(callback); + } + }); + } +} diff --git a/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/ChromiumTestShellTestBase.java b/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/ChromiumTestShellTestBase.java index c982a72..4ad0645 100644 --- a/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/ChromiumTestShellTestBase.java +++ b/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/ChromiumTestShellTestBase.java @@ -5,14 +5,18 @@ package org.chromium.chrome.testshell; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.net.Uri; import android.test.ActivityInstrumentationTestCase2; import android.text.TextUtils; +import org.chromium.base.ThreadUtils; import org.chromium.chrome.test.util.ApplicationData; +import org.chromium.content.browser.BrowserStartupController; import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.CriteriaHelper; +import org.chromium.content.common.CommandLine; import java.util.concurrent.atomic.AtomicBoolean; @@ -28,6 +32,17 @@ public class ChromiumTestShellTestBase extends super(ChromiumTestShellActivity.class); } + protected static void startChromeBrowserProcessSync(final Context targetContext) { + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + CommandLine.initFromFile("/data/local/tmp/chromium-testshell-command-line"); + BrowserStartupController.get(targetContext).startBrowserProcessesSync( + BrowserStartupController.MAX_RENDERERS_LIMIT); + } + }); + } + /** * Starts the ChromiumTestShell activity and loads the given URL. */ diff --git a/chrome/browser/profiles/profile_android.cc b/chrome/browser/profiles/profile_android.cc index 71df381..5f0eda1 100644 --- a/chrome/browser/profiles/profile_android.cc +++ b/chrome/browser/profiles/profile_android.cc @@ -6,6 +6,7 @@ #include "base/android/jni_android.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" #include "jni/Profile_jni.h" using base::android::AttachCurrentThread; @@ -45,6 +46,28 @@ bool ProfileAndroid::RegisterProfileAndroid(JNIEnv* env) { return RegisterNativesImpl(env); } +// static +jobject ProfileAndroid::GetLastUsedProfile(JNIEnv* env, jclass clazz) { + Profile* profile = ProfileManager::GetLastUsedProfile(); + if (profile == NULL) { + NOTREACHED() << "Profile not found."; + return NULL; + } + + ProfileAndroid* profile_android = ProfileAndroid::FromProfile(profile); + if (profile_android == NULL) { + NOTREACHED() << "ProfileAndroid not found."; + return NULL; + } + + return profile_android->obj_.obj(); +} + +// static +jobject GetLastUsedProfile(JNIEnv* env, jclass clazz) { + return ProfileAndroid::GetLastUsedProfile(env, clazz); +} + ProfileAndroid::ProfileAndroid(Profile* profile) : profile_(profile) { JNIEnv* env = AttachCurrentThread(); diff --git a/chrome/browser/profiles/profile_android.h b/chrome/browser/profiles/profile_android.h index 780dde0..0017b5b 100644 --- a/chrome/browser/profiles/profile_android.h +++ b/chrome/browser/profiles/profile_android.h @@ -21,6 +21,8 @@ class ProfileAndroid : public base::SupportsUserData::Data { static Profile* FromProfileAndroid(jobject obj); static bool RegisterProfileAndroid(JNIEnv* env); + static jobject GetLastUsedProfile(JNIEnv* env, jclass clazz); + explicit ProfileAndroid(Profile* profile); virtual ~ProfileAndroid(); diff --git a/chrome/browser/signin/DEPS b/chrome/browser/signin/DEPS index 2b4bc3803..9ada978 100644 --- a/chrome/browser/signin/DEPS +++ b/chrome/browser/signin/DEPS @@ -12,6 +12,7 @@ include_rules = [ "!chrome/browser/policy/cloud/user_policy_signin_service.h", "!chrome/browser/policy/cloud/user_policy_signin_service_factory.h", "!chrome/browser/profiles/profile.h", + "!chrome/browser/profiles/profile_android.h", "!chrome/browser/profiles/profile_info_cache.h", "!chrome/browser/profiles/profile_io_data.h", "!chrome/browser/profiles/profile_manager.h", diff --git a/chrome/browser/signin/android_profile_oauth2_token_service.cc b/chrome/browser/signin/android_profile_oauth2_token_service.cc index 6883533..60d378d 100644 --- a/chrome/browser/signin/android_profile_oauth2_token_service.cc +++ b/chrome/browser/signin/android_profile_oauth2_token_service.cc @@ -9,11 +9,13 @@ #include "base/android/jni_string.h" #include "base/bind.h" #include "base/logging.h" +#include "chrome/browser/profiles/profile_android.h" +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/sync/profile_sync_service_android.h" #include "content/public/browser/browser_thread.h" -#include "jni/AndroidProfileOAuth2TokenServiceHelper_jni.h" +#include "jni/OAuth2TokenService_jni.h" using base::android::AttachCurrentThread; using base::android::ConvertJavaStringToUTF8; @@ -49,9 +51,28 @@ typedef base::Callback<void( } // namespace AndroidProfileOAuth2TokenService::AndroidProfileOAuth2TokenService() { + JNIEnv* env = AttachCurrentThread(); + base::android::ScopedJavaLocalRef<jobject> local_java_ref = + Java_OAuth2TokenService_create(env, reinterpret_cast<int>(this)); + java_ref_.Reset(env, local_java_ref.obj()); } -AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() { +AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() {} + +// static +jobject AndroidProfileOAuth2TokenService::GetForProfile( + JNIEnv* env, jclass clazz, jobject j_profile_android) { + Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android); + AndroidProfileOAuth2TokenService* service = + ProfileOAuth2TokenServiceFactory::GetForProfile(profile); + return service->java_ref_.obj(); +} + +static jobject GetForProfile(JNIEnv* env, + jclass clazz, + jobject j_profile_android) { + return AndroidProfileOAuth2TokenService::GetForProfile( + env, clazz, j_profile_android); } bool AndroidProfileOAuth2TokenService::RefreshTokenIsAvailable( @@ -60,7 +81,7 @@ bool AndroidProfileOAuth2TokenService::RefreshTokenIsAvailable( ScopedJavaLocalRef<jstring> j_account_id = ConvertUTF8ToJavaString(env, account_id); jboolean refresh_token_is_available = - Java_AndroidProfileOAuth2TokenServiceHelper_hasOAuth2RefreshToken( + Java_OAuth2TokenService_hasOAuth2RefreshToken( env, base::android::GetApplicationContext(), j_account_id.obj()); return refresh_token_is_available != JNI_FALSE; @@ -70,7 +91,7 @@ std::vector<std::string> AndroidProfileOAuth2TokenService::GetAccounts() { std::vector<std::string> accounts; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobjectArray> j_accounts = - Java_AndroidProfileOAuth2TokenServiceHelper_getAccounts( + Java_OAuth2TokenService_getAccounts( env, base::android::GetApplicationContext()); // TODO(fgorski): We may decide to filter out some of the accounts. base::android::AppendJavaStringArrayToStringVector(env, @@ -104,7 +125,7 @@ void AndroidProfileOAuth2TokenService::FetchOAuth2Token( request->AsWeakPtr()))); // Call into Java to get a new token. - Java_AndroidProfileOAuth2TokenServiceHelper_getOAuth2AuthToken( + Java_OAuth2TokenService_getOAuth2AuthToken( env, base::android::GetApplicationContext(), j_username.obj(), j_scope.obj(), @@ -124,11 +145,91 @@ void AndroidProfileOAuth2TokenService::InvalidateOAuth2Token( JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> j_access_token = ConvertUTF8ToJavaString(env, access_token); - Java_AndroidProfileOAuth2TokenServiceHelper_invalidateOAuth2AuthToken( + Java_OAuth2TokenService_invalidateOAuth2AuthToken( env, base::android::GetApplicationContext(), j_access_token.obj()); } +void AndroidProfileOAuth2TokenService::ValidateAccounts(JNIEnv* env, + jobject obj, + jobjectArray accounts, + jstring j_current_acc) { + std::vector<std::string> account_ids; + base::android::AppendJavaStringArrayToStringVector(env, + accounts, + &account_ids); + std::string signed_in_account = ConvertJavaStringToUTF8(env, j_current_acc); + + if (signed_in_account.empty()) + return; + + if (std::find(account_ids.begin(), + account_ids.end(), + signed_in_account) != account_ids.end()) { + // Currently signed in account still exists among accounts on system. + FireRefreshTokenAvailable(signed_in_account); + } else { + // Currently signed in account does not any longer exist among accounts on + // system. + FireRefreshTokenRevoked(signed_in_account); + } +} + +void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava( + JNIEnv* env, + jobject obj, + const jstring account_name) { + std::string account_id = ConvertJavaStringToUTF8(env, account_name); + AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id); +} + +void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable( + const std::string& account_id) { + // Notify native observers. + OAuth2TokenService::FireRefreshTokenAvailable(account_id); + // Notify Java observers. + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> account_name = + ConvertUTF8ToJavaString(env, account_id); + Java_OAuth2TokenService_notifyRefreshTokenAvailable( + env, java_ref_.obj(), account_name.obj()); +} + +void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava( + JNIEnv* env, + jobject obj, + const jstring account_name) { + std::string account_id = ConvertJavaStringToUTF8(env, account_name); + AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id); +} + +void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked( + const std::string& account_id) { + // Notify native observers. + OAuth2TokenService::FireRefreshTokenRevoked(account_id); + // Notify Java observers. + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> account_name = + ConvertUTF8ToJavaString(env, account_id); + Java_OAuth2TokenService_notifyRefreshTokenRevoked( + env, java_ref_.obj(), account_name.obj()); +} + +void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava( + JNIEnv* env, + jobject obj) { + AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded(); +} + +void AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded() { + // Notify native observers. + OAuth2TokenService::FireRefreshTokensLoaded(); + // Notify Java observers. + JNIEnv* env = AttachCurrentThread(); + Java_OAuth2TokenService_notifyRefreshTokensLoaded( + env, java_ref_.obj()); +} + // 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, jclass clazz, diff --git a/chrome/browser/signin/android_profile_oauth2_token_service.h b/chrome/browser/signin/android_profile_oauth2_token_service.h index 6c44377..1d6c054 100644 --- a/chrome/browser/signin/android_profile_oauth2_token_service.h +++ b/chrome/browser/signin/android_profile_oauth2_token_service.h @@ -28,16 +28,43 @@ class TokenService; // request from other thread, please use ProfileOAuth2TokenServiceRequest. class AndroidProfileOAuth2TokenService : public ProfileOAuth2TokenService { public: - virtual bool RefreshTokenIsAvailable( - const std::string& account_id) OVERRIDE; - // Registers the AndroidProfileOAuth2TokenService's native methods through // JNI. static bool Register(JNIEnv* env); + // Creates a new instance of the AndroidProfileOAuth2TokenService. + static AndroidProfileOAuth2TokenService* Create(); + + // Returns a reference to the Java instance of this service. + static jobject GetForProfile( + JNIEnv* env, jclass clazz, jobject j_profile_android); + + virtual bool RefreshTokenIsAvailable( + const std::string& account_id) OVERRIDE; + // Lists account IDs of all accounts with a refresh token. virtual std::vector<std::string> GetAccounts() OVERRIDE; + void ValidateAccounts(JNIEnv* env, + jobject obj, + jobjectArray accounts, + jstring current_account); + + // Triggers a notification to all observers of the OAuth2TokenService that a + // refresh token is now available. This may cause observers to retry + // operations that require authentication. + virtual void FireRefreshTokenAvailableFromJava(JNIEnv* env, + jobject obj, + const jstring account_name); + // Triggers a notification to all observers of the OAuth2TokenService that a + // refresh token is now available. + virtual void FireRefreshTokenRevokedFromJava(JNIEnv* env, + jobject obj, + const jstring account_name); + // Triggers a notification to all observers of the OAuth2TokenService that all + // refresh tokens have now been loaded. + virtual void FireRefreshTokensLoadedFromJava(JNIEnv* env, jobject obj); + protected: friend class ProfileOAuth2TokenServiceFactory; AndroidProfileOAuth2TokenService(); @@ -59,7 +86,17 @@ class AndroidProfileOAuth2TokenService : public ProfileOAuth2TokenService { const ScopeSet& scopes, const std::string& access_token) OVERRIDE; + // Called to notify observers when a refresh token is available. + virtual void FireRefreshTokenAvailable( + const std::string& account_id) OVERRIDE; + // Called to notify observers when a refresh token has been revoked. + virtual void FireRefreshTokenRevoked(const std::string& account_id) OVERRIDE; + // Called to notify observers when refresh tokans have been loaded. + virtual void FireRefreshTokensLoaded() OVERRIDE; + private: + base::android::ScopedJavaGlobalRef<jobject> java_ref_; + DISALLOW_COPY_AND_ASSIGN(AndroidProfileOAuth2TokenService); }; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 21caa33..0ad6c8d 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -3637,7 +3637,7 @@ 'android/java/src/org/chromium/chrome/browser/ShortcutHelper.java', '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/AndroidProfileOAuth2TokenServiceHelper.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/sync/ProfileSyncService.java', 'android/java/src/org/chromium/chrome/browser/TabBase.java', diff --git a/google_apis/gaia/oauth2_token_service.h b/google_apis/gaia/oauth2_token_service.h index b8faecd..52ea3ad 100644 --- a/google_apis/gaia/oauth2_token_service.h +++ b/google_apis/gaia/oauth2_token_service.h @@ -223,9 +223,9 @@ class OAuth2TokenService : public base::NonThreadSafe { void CancelRequestsForAccount(const std::string& account_id); // Called by subclasses to notify observers. - void FireRefreshTokenAvailable(const std::string& account_id); - void FireRefreshTokenRevoked(const std::string& account_id); - void FireRefreshTokensLoaded(); + virtual void FireRefreshTokenAvailable(const std::string& account_id); + virtual void FireRefreshTokenRevoked(const std::string& account_id); + virtual void FireRefreshTokensLoaded(); // Creates a request implementation. Can be overriden by derived classes to // provide additional control of token consumption. |consumer| will outlive |