14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file.
44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)package org.chromium.chrome.browser.signin;
64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import android.accounts.Account;
84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import android.app.Activity;
94e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import android.content.Context;
105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuimport android.preference.PreferenceManager;
114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import android.util.Log;
124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import org.chromium.base.CalledByNative;
144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import org.chromium.base.ObserverList;
154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import org.chromium.base.ThreadUtils;
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport org.chromium.base.VisibleForTesting;
174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import org.chromium.chrome.browser.profiles.Profile;
184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import org.chromium.sync.signin.AccountManagerHelper;
194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import org.chromium.sync.signin.ChromeSigninController;
204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuimport java.util.Arrays;
22f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)import java.util.HashSet;
23f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)import java.util.Set;
244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import java.util.concurrent.Semaphore;
254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import java.util.concurrent.TimeUnit;
264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import java.util.concurrent.atomic.AtomicReference;
274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)import javax.annotation.Nullable;
294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Java instance for the native OAuth2TokenService.
324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * <p/>
334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * This class forwards calls to request or invalidate access tokens made by native code to
344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * AccountManagerHelper and forwards callbacks to native code.
354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * <p/>
364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)public final class OAuth2TokenService {
384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private static final String TAG = "OAuth2TokenService";
404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    @VisibleForTesting
425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    public static final String STORED_ACCOUNTS_KEY = "google.services.stored_accounts";
435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
44f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    /**
45f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     * Classes that want to listen for refresh token availability should
46f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     * implement this interface and register with {@link #addObserver}.
47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)     */
484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public interface OAuth2TokenServiceObserver {
494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        void onRefreshTokenAvailable(Account account);
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        void onRefreshTokenRevoked(Account account);
514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        void onRefreshTokensLoaded();
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private static final String OAUTH2_SCOPE_PREFIX = "oauth2:";
554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    private final long mNativeProfileOAuth2TokenService;
574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private final ObserverList<OAuth2TokenServiceObserver> mObservers;
584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    private OAuth2TokenService(long nativeOAuth2Service) {
604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        mNativeProfileOAuth2TokenService = nativeOAuth2Service;
614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        mObservers = new ObserverList<OAuth2TokenServiceObserver>();
624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public static OAuth2TokenService getForProfile(Profile profile) {
654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        ThreadUtils.assertOnUiThread();
664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        return (OAuth2TokenService) nativeGetForProfile(profile);
674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    @CalledByNative
70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    private static OAuth2TokenService create(long nativeOAuth2Service) {
714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        ThreadUtils.assertOnUiThread();
724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        return new OAuth2TokenService(nativeOAuth2Service);
734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public void addObserver(OAuth2TokenServiceObserver observer) {
764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        ThreadUtils.assertOnUiThread();
774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        mObservers.addObserver(observer);
784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public void removeObserver(OAuth2TokenServiceObserver observer) {
814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        ThreadUtils.assertOnUiThread();
824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        mObservers.removeObserver(observer);
834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private static Account getAccountOrNullFromUsername(Context context, String username) {
864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        if (username == null) {
874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            Log.e(TAG, "Username is null");
884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            return null;
894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        }
904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context);
924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        Account account = accountManagerHelper.getAccountFromName(username);
934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        if (account == null) {
944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            Log.e(TAG, "Account not found for provided username.");
954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            return null;
964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        }
974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        return account;
984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    /**
1015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu     * Called by native to list the activite accounts in the OS.
1024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     */
1035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    @VisibleForTesting
1044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    @CalledByNative
1055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    public static String[] getSystemAccounts(Context context) {
1064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context);
1074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        java.util.List<String> accountNames = accountManagerHelper.getGoogleAccountNames();
1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        return accountNames.toArray(new String[accountNames.size()]);
1094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
1104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    /**
1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu     * Called by native to list the accounts with OAuth2 refresh tokens.
1135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu     * This can differ from getSystemAccounts as the user add/remove accounts
1145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu     * from the OS. validateAccounts should be called to keep these two
1155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu     * in sync.
1165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu     */
1175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    @CalledByNative
1185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    public static String[] getAccounts(Context context) {
1195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        return getStoredAccounts(context);
1205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
1215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    /**
1234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * Called by native to retrieve OAuth2 tokens.
1244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     *
1254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param username The native username (full address).
1264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix).
1274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param nativeCallback The pointer to the native callback that should be run upon completion.
1284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     */
1294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    @CalledByNative
1304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public static void getOAuth2AuthToken(
131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            Context context, String username, String scope, final long nativeCallback) {
1324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        Account account = getAccountOrNullFromUsername(context, username);
1334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        if (account == null) {
1344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            nativeOAuth2TokenFetched(null, false, nativeCallback);
1354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            return;
1364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        }
1374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope;
1384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context);
1404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        accountManagerHelper.getAuthTokenFromForeground(
1414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            null, account, oauth2Scope, new AccountManagerHelper.GetAuthTokenCallback() {
1424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                @Override
1434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                public void tokenAvailable(String token) {
1444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                    nativeOAuth2TokenFetched(
1454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                        token, token != null, nativeCallback);
1464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                }
1474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            });
1484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
1494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    /**
1514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * Call this method to retrieve an OAuth2 access token for the given account and scope.
1524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     *
1534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param activity the current activity. May be null.
1544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param account the account to get the access token for.
1554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix).
1564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param callback called on successful and unsuccessful fetching of auth token.
1574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     */
1584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public static void getOAuth2AccessToken(Context context, @Nullable Activity activity,
1594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                            Account account, String scope,
1604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                            AccountManagerHelper.GetAuthTokenCallback callback) {
1614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope;
1624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        AccountManagerHelper.get(context).getAuthTokenFromForeground(
1634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                activity, account, oauth2Scope, callback);
1644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
1654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    /**
1674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * Call this method to retrieve an OAuth2 access token for the given account and scope. This
1684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * method times out after the specified timeout, and will return null if that happens.
1694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     *
1704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * Given that this is a blocking method call, this should never be called from the UI thread.
1714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     *
1724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param activity the current activity. May be null.
1734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param account the account to get the access token for.
1744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix).
1754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param timeout the timeout.
1764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * @param unit the unit for |timeout|.
1774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     */
1784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public static String getOAuth2AccessTokenWithTimeout(
1794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            Context context, @Nullable Activity activity, Account account, String scope,
1804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            long timeout, TimeUnit unit) {
1814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        assert !ThreadUtils.runningOnUiThread();
1824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        final AtomicReference<String> result = new AtomicReference<String>();
1834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        final Semaphore semaphore = new Semaphore(0);
1844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        getOAuth2AccessToken(
1854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                context, activity, account, scope,
1864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                new AccountManagerHelper.GetAuthTokenCallback() {
1874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                    @Override
1884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                    public void tokenAvailable(String token) {
1894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                        result.set(token);
1904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                        semaphore.release();
1914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                    }
1924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                });
1934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        try {
1944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            if (semaphore.tryAcquire(timeout, unit)) {
1954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                return result.get();
1964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            } else {
1974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                Log.d(TAG, "Failed to retrieve auth token within timeout (" +
1984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                        timeout + " + " + unit.name() + ")");
1994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                return null;
2004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            }
2014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        } catch (InterruptedException e) {
2024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            Log.w(TAG, "Got interrupted while waiting for auth token");
2034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            return null;
2044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        }
2054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    /**
2084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * Called by native to check wether the account has an OAuth2 refresh token.
2094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     */
2104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    @CalledByNative
2114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public static boolean hasOAuth2RefreshToken(Context context, String accountName) {
2124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        return AccountManagerHelper.get(context).hasAccountForName(accountName);
2134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    /**
2164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    * Called by native to invalidate an OAuth2 token.
2174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    */
2184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    @CalledByNative
2194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public static void invalidateOAuth2AuthToken(Context context, String accessToken) {
2204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        if (accessToken != null) {
2214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            AccountManagerHelper.get(context).invalidateAuthToken(accessToken);
2224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        }
2234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
225f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    @CalledByNative
226f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    public void validateAccounts(Context context, boolean forceNotifications) {
2274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        ThreadUtils.assertOnUiThread();
2284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        String currentlySignedInAccount =
2294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                ChromeSigninController.get(context).getSignedInAccountName();
230f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        nativeValidateAccounts(mNativeProfileOAuth2TokenService, currentlySignedInAccount,
231f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                               forceNotifications);
2324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    /**
2354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * Triggers a notification to all observers of the native and Java instance of the
2364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * OAuth2TokenService that a refresh token is now available. This may cause observers to retry
2374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * operations that require authentication.
2384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     */
2394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public void fireRefreshTokenAvailable(Account account) {
2404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        ThreadUtils.assertOnUiThread();
2414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        assert account != null;
2424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        nativeFireRefreshTokenAvailableFromJava(mNativeProfileOAuth2TokenService, account.name);
2434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    @CalledByNative
2464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public void notifyRefreshTokenAvailable(String accountName) {
2474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        assert accountName != null;
2484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        Account account = AccountManagerHelper.createAccountFromName(accountName);
2494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        for (OAuth2TokenServiceObserver observer : mObservers) {
2504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            observer.onRefreshTokenAvailable(account);
2514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        }
2524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    /**
2554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * Triggers a notification to all observers of the native and Java instance of the
2564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * OAuth2TokenService that a refresh token is now revoked.
2574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     */
2584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public void fireRefreshTokenRevoked(Account account) {
2594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        ThreadUtils.assertOnUiThread();
2604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        assert account != null;
2614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        nativeFireRefreshTokenRevokedFromJava(mNativeProfileOAuth2TokenService, account.name);
2624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    @CalledByNative
2654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public void notifyRefreshTokenRevoked(String accountName) {
2664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        assert accountName != null;
2674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        Account account = AccountManagerHelper.createAccountFromName(accountName);
2684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        for (OAuth2TokenServiceObserver observer : mObservers) {
2694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            observer.onRefreshTokenRevoked(account);
2704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        }
2714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    /**
2744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * Triggers a notification to all observers of the native and Java instance of the
2754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     * OAuth2TokenService that all refresh tokens now have been loaded.
2764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)     */
2774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public void fireRefreshTokensLoaded() {
2784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        ThreadUtils.assertOnUiThread();
2794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        nativeFireRefreshTokensLoadedFromJava(mNativeProfileOAuth2TokenService);
2804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    @CalledByNative
2834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public void notifyRefreshTokensLoaded() {
2844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        for (OAuth2TokenServiceObserver observer : mObservers) {
2854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            observer.onRefreshTokensLoaded();
2864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        }
2874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
2884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    private static String[] getStoredAccounts(Context context) {
2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        Set<String> accounts =
2915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                PreferenceManager.getDefaultSharedPreferences(context)
2925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                        .getStringSet(STORED_ACCOUNTS_KEY, null);
2935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        return accounts == null ? new String[]{} : accounts.toArray(new String[accounts.size()]);
2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
2955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    @CalledByNative
2975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    private static void saveStoredAccounts(Context context, String[] accounts) {
2985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        Set<String> set = new HashSet<String>(Arrays.asList(accounts));
2995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        PreferenceManager.getDefaultSharedPreferences(context).edit().
3005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                putStringSet(STORED_ACCOUNTS_KEY, set).apply();
3015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
3025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private static native Object nativeGetForProfile(Profile profile);
3044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private static native void nativeOAuth2TokenFetched(
305f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            String authToken, boolean result, long nativeCallback);
306f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    private native void nativeValidateAccounts(
307f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            long nativeAndroidProfileOAuth2TokenService,
308f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            String currentlySignedInAccount,
309f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            boolean forceNotifications);
3104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private native void nativeFireRefreshTokenAvailableFromJava(
311f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            long nativeAndroidProfileOAuth2TokenService, String accountName);
3124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private native void nativeFireRefreshTokenRevokedFromJava(
313f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            long nativeAndroidProfileOAuth2TokenService, String accountName);
3144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    private native void nativeFireRefreshTokensLoadedFromJava(
315f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            long nativeAndroidProfileOAuth2TokenService);
3164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
317