EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.sync.test.util]

COVERAGE SUMMARY FOR SOURCE FILE [MockAccountManager.java]

nameclass, %method, %block, %line, %
MockAccountManager.java54%  (7/13)34%  (24/71)25%  (315/1267)28%  (65/236)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class MockAccountManager$10%   (0/1)0%   (0/2)0%   (0/9)0%   (0/2)
MockAccountManager$1 (MockAccountManager): void 0%   (0/1)0%   (0/6)0%   (0/1)
call (): Boolean 0%   (0/1)0%   (0/3)0%   (0/1)
     
class MockAccountManager$20%   (0/1)0%   (0/2)0%   (0/34)0%   (0/6)
MockAccountManager$2 (MockAccountManager, AccountHolder, boolean): void 0%   (0/1)0%   (0/12)0%   (0/1)
call (): Bundle 0%   (0/1)0%   (0/22)0%   (0/5)
     
class MockAccountManager$40%   (0/1)0%   (0/2)0%   (0/20)0%   (0/4)
MockAccountManager$4 (MockAccountManager, Intent): void 0%   (0/1)0%   (0/9)0%   (0/1)
call (): Bundle 0%   (0/1)0%   (0/11)0%   (0/3)
     
class MockAccountManager$50%   (0/1)0%   (0/2)0%   (0/15)0%   (0/3)
MockAccountManager$5 (AccountManagerCallback, AccountManagerFuture): void 0%   (0/1)0%   (0/9)0%   (0/1)
run (): void 0%   (0/1)0%   (0/6)0%   (0/2)
     
class MockAccountManager$60%   (0/1)0%   (0/2)0%   (0/26)0%   (0/5)
MockAccountManager$6 (MockAccountManager, Object): void 0%   (0/1)0%   (0/9)0%   (0/1)
onReceive (Context, Intent): void 0%   (0/1)0%   (0/17)0%   (0/4)
     
class MockAccountManager$AccountAuthTokenPreparation0%   (0/1)0%   (0/6)0%   (0/51)0%   (0/10)
MockAccountManager$AccountAuthTokenPreparation (Account, String, boolean): void 0%   (0/1)0%   (0/12)0%   (0/5)
MockAccountManager$AccountAuthTokenPreparation (Account, String, boolean, Moc... 0%   (0/1)0%   (0/6)0%   (0/1)
getAccount (): Account 0%   (0/1)0%   (0/3)0%   (0/1)
getAuthTokenType (): String 0%   (0/1)0%   (0/3)0%   (0/1)
isAllowed (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
toString (): String 0%   (0/1)0%   (0/24)0%   (0/1)
     
class MockAccountManager100% (1/1)32%  (12/38)23%  (181/775)26%  (34.8/133)
access$200 (Handler, AccountManagerCallback, AccountManagerFuture): void 0%   (0/1)0%   (0/5)0%   (0/1)
access$300 (MockAccountManager): Handler 0%   (0/1)0%   (0/3)0%   (0/1)
access$500 (MockAccountManager, Context, Intent): void 0%   (0/1)0%   (0/5)0%   (0/1)
access$600 (MockAccountManager, MockAccountManager$AccountAuthTokenPreparatio... 0%   (0/1)0%   (0/4)0%   (0/1)
access$700 (MockAccountManager, Account): AccountHolder 0%   (0/1)0%   (0/4)0%   (0/1)
access$800 (MockAccountManager, AccountHolder, String): String 0%   (0/1)0%   (0/5)0%   (0/1)
addAccountExplicitly (Account, String, Bundle): boolean 0%   (0/1)0%   (0/11)0%   (0/2)
addPreparedAppPermission (MockAccountManager$AccountAuthTokenPreparation): void 0%   (0/1)0%   (0/17)0%   (0/3)
applyPreparedPermission (MockAccountManager$AccountAuthTokenPreparation): void 0%   (0/1)0%   (0/32)0%   (0/5)
blockingGetAuthToken (Account, String, boolean): String 0%   (0/1)0%   (0/37)0%   (0/8)
clearPassword (Account): void 0%   (0/1)0%   (0/5)0%   (0/2)
confirmCredentials (Account, Bundle, Activity, AccountManagerCallback, Handle... 0%   (0/1)0%   (0/36)0%   (0/6)
getAccounts (): Account [] 0%   (0/1)0%   (0/4)0%   (0/1)
getAccountsByType (String): Account [] 0%   (0/1)0%   (0/49)0%   (0/9)
getAuthToken (Account, String, Bundle, Activity, AccountManagerCallback, Hand... 0%   (0/1)0%   (0/8)0%   (0/1)
getAuthenticatorTypes (): AuthenticatorDescription [] 0%   (0/1)0%   (0/17)0%   (0/2)
getPassword (Account): String 0%   (0/1)0%   (0/5)0%   (0/1)
invalidateAuthToken (String, String): void 0%   (0/1)0%   (0/41)0%   (0/8)
newGrantCredentialsPermissionIntent (boolean, Account, String): Intent 0%   (0/1)0%   (0/32)0%   (0/7)
peekAuthToken (Account, String): String 0%   (0/1)0%   (0/6)0%   (0/1)
postToHandler (Handler, AccountManagerCallback, AccountManagerFuture): void 0%   (0/1)0%   (0/9)0%   (0/2)
prepareAllowAppPermission (Account, String): void 0%   (0/1)0%   (0/10)0%   (0/2)
prepareDenyAppPermission (Account, String): void 0%   (0/1)0%   (0/10)0%   (0/2)
removeAccount (Account, AccountManagerCallback, Handler): AccountManagerFuture 0%   (0/1)0%   (0/23)0%   (0/3)
setPassword (Account, String): void 0%   (0/1)0%   (0/10)0%   (0/2)
waitForActivity (Context, Intent): void 0%   (0/1)0%   (0/69)0%   (0/16)
internalGenerateAndStoreAuthToken (AccountHolder, String): String 100% (1/1)28%  (16/58)38%  (3.1/8)
getPreparedPermission (Account, String): MockAccountManager$AccountAuthTokenP... 100% (1/1)35%  (9/26)36%  (1.4/4)
getAuthTokenFuture (Account, String, Activity, AccountManagerCallback, Handle... 100% (1/1)42%  (32/76)57%  (4/7)
getAccountHolder (Account): AccountHolder 100% (1/1)53%  (20/38)61%  (3.7/6)
MockAccountManager (Context, Context, Account []): void 100% (1/1)73%  (44/60)85%  (9.4/11)
access$000 (Account, String): Bundle 100% (1/1)100% (4/4)100% (1/1)
access$400 (MockAccountManager, Account, String): MockAccountManager$AccountA... 100% (1/1)100% (5/5)100% (1/1)
addAccountHolderExplicitly (AccountHolder): boolean 100% (1/1)100% (9/9)100% (3/3)
getAuthToken (Account, String, boolean, AccountManagerCallback, Handler): Acc... 100% (1/1)100% (8/8)100% (1/1)
getAuthTokenBundle (Account, String): Bundle 100% (1/1)100% (20/20)100% (5/5)
postAsyncAccountChangedEvent (): void 100% (1/1)100% (9/9)100% (2/2)
runTask (Executor, MockAccountManager$AccountManagerTask): AccountManagerFuture 100% (1/1)100% (5/5)100% (2/2)
     
class MockAccountManager$AccountManagerTask100% (1/1)57%  (4/7)27%  (41/153)28%  (11.2/40)
getHandler (): Handler 0%   (0/1)0%   (0/10)0%   (0/1)
getResult (long, TimeUnit): Object 0%   (0/1)0%   (0/5)0%   (0/1)
run (): void 0%   (0/1)0%   (0/11)0%   (0/5)
internalGetResult (long, TimeUnit): Object 100% (1/1)14%  (13/93)10%  (2.2/23)
done (): void 100% (1/1)40%  (4/10)67%  (2/3)
MockAccountManager$AccountManagerTask (MockAccountManager, Handler, AccountMa... 100% (1/1)100% (19/19)100% (6/6)
getResult (): Object 100% (1/1)100% (5/5)100% (1/1)
     
class MockAccountManager$AccountManagerAuthTokenTask100% (1/1)67%  (2/3)33%  (42/128)46%  (13/28)
generateResult (AccountHolder, String): void 0%   (0/1)0%   (0/23)0%   (0/6)
run (): void 100% (1/1)21%  (17/80)40%  (6/15)
MockAccountManager$AccountManagerAuthTokenTask (MockAccountManager, Activity,... 100% (1/1)100% (25/25)100% (7/7)
     
class MockAccountManager$AccountManagerTask$1100% (1/1)50%  (1/2)55%  (6/11)50%  (1/2)
call (): Object 0%   (0/1)0%   (0/5)0%   (0/1)
MockAccountManager$AccountManagerTask$1 (MockAccountManager): void 100% (1/1)100% (6/6)100% (1/1)
     
class MockAccountManager$3100% (1/1)100% (2/2)100% (19/19)100% (2/2)
MockAccountManager$3 (MockAccountManager, AccountHolder, String): void 100% (1/1)100% (12/12)100% (1/1)
call (): Bundle 100% (1/1)100% (7/7)100% (1/1)
     
class MockAccountManager$7100% (1/1)100% (2/2)100% (16/16)100% (3/3)
MockAccountManager$7 (MockAccountManager): void 100% (1/1)100% (6/6)100% (1/1)
doInBackground (Void []): Void 100% (1/1)100% (10/10)100% (2/2)
     
class MockAccountManager$SingleThreadedExecutor100% (1/1)100% (1/1)100% (10/10)100% (2/2)
MockAccountManager$SingleThreadedExecutor (): void 100% (1/1)100% (10/10)100% (2/2)

1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4 
5package org.chromium.sync.test.util;
6 
7import android.accounts.Account;
8import android.accounts.AccountManager;
9import android.accounts.AccountManagerCallback;
10import android.accounts.AccountManagerFuture;
11import android.accounts.AuthenticatorDescription;
12import android.accounts.AuthenticatorException;
13import android.accounts.OperationCanceledException;
14import android.app.Activity;
15import android.content.BroadcastReceiver;
16import android.content.ComponentName;
17import android.content.Context;
18import android.content.Intent;
19import android.content.IntentFilter;
20import android.os.AsyncTask;
21import android.os.Bundle;
22import android.os.Handler;
23import android.util.Log;
24 
25import org.chromium.sync.signin.AccountManagerDelegate;
26import org.chromium.sync.signin.AccountManagerHelper;
27 
28import java.io.IOException;
29import java.util.HashSet;
30import java.util.LinkedList;
31import java.util.List;
32import java.util.Set;
33import java.util.UUID;
34import java.util.concurrent.Callable;
35import java.util.concurrent.CancellationException;
36import java.util.concurrent.ExecutionException;
37import java.util.concurrent.Executor;
38import java.util.concurrent.FutureTask;
39import java.util.concurrent.LinkedBlockingDeque;
40import java.util.concurrent.ThreadPoolExecutor;
41import java.util.concurrent.TimeUnit;
42import java.util.concurrent.TimeoutException;
43 
44import javax.annotation.Nullable;
45 
46/**
47 * The MockAccountManager helps out if you want to mock out all calls to the Android AccountManager.
48 *
49 * You should provide a set of accounts as a constructor argument, or use the more direct approach
50 * and provide an array of AccountHolder objects.
51 *
52 * Currently, this implementation supports adding and removing accounts, handling credentials
53 * (including confirming them), and handling of dummy auth tokens.
54 *
55 * If you want the MockAccountManager to popup an activity for granting/denying access to an
56 * authtokentype for a given account, use prepareGrantAppPermission(...).
57 *
58 * If you want to auto-approve a given authtokentype, use addAccountHolderExplicitly(...) with
59 * an AccountHolder you have built with hasBeenAccepted("yourAuthTokenType", true).
60 *
61 * If you want to auto-approve all auth token types for a given account, use the {@link
62 * AccountHolder} builder method alwaysAccept(true).
63 */
64public class MockAccountManager implements AccountManagerDelegate {
65 
66    private static final String TAG = "MockAccountManager";
67 
68    private static final int WAIT_TIME_FOR_GRANT_BROADCAST_MS = 20000;
69 
70    static final String MUTEX_WAIT_ACTION =
71            "org.chromium.sync.test.util.MockAccountManager.MUTEX_WAIT_ACTION";
72 
73    protected final Context mContext;
74 
75    private final Context mTestContext;
76 
77    private final Set<AccountHolder> mAccounts;
78 
79    private final List<AccountAuthTokenPreparation> mAccountPermissionPreparations;
80 
81    private final Handler mMainHandler;
82 
83    private final SingleThreadedExecutor mExecutor;
84 
85    public MockAccountManager(Context context, Context testContext, Account... accounts) {
86        mContext = context;
87        // The manifest that is backing testContext needs to provide the
88        // MockGrantCredentialsPermissionActivity.
89        mTestContext = testContext;
90        mMainHandler = new Handler(mContext.getMainLooper());
91        mExecutor = new SingleThreadedExecutor();
92        mAccounts = new HashSet<AccountHolder>();
93        mAccountPermissionPreparations = new LinkedList<AccountAuthTokenPreparation>();
94        if (accounts != null) {
95            for (Account account : accounts) {
96                mAccounts.add(AccountHolder.create().account(account).alwaysAccept(true).build());
97            }
98        }
99    }
100 
101    private static class SingleThreadedExecutor extends ThreadPoolExecutor {
102        public SingleThreadedExecutor() {
103            super(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
104        }
105    }
106 
107    @Override
108    public Account[] getAccounts() {
109        return getAccountsByType(null);
110    }
111 
112    @Override
113    public Account[] getAccountsByType(@Nullable String type) {
114        if(!AccountManagerHelper.GOOGLE_ACCOUNT_TYPE.equals(type)) {
115            throw new IllegalArgumentException("Invalid account type: " + type);
116        }
117        if (mAccounts == null) {
118            return new Account[0];
119        } else {
120            Account[] accounts = new Account[mAccounts.size()];
121            int i = 0;
122            for (AccountHolder ah : mAccounts) {
123                accounts[i++] = ah.getAccount();
124            }
125            return accounts;
126        }
127    }
128 
129    @Override
130    public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
131        AccountHolder accountHolder =
132                AccountHolder.create().account(account).password(password).build();
133        return addAccountHolderExplicitly(accountHolder);
134    }
135 
136    public boolean addAccountHolderExplicitly(AccountHolder accountHolder) {
137        boolean result = mAccounts.add(accountHolder);
138        postAsyncAccountChangedEvent();
139        return result;
140    }
141 
142    @Override
143    public AccountManagerFuture<Boolean> removeAccount(Account account,
144            AccountManagerCallback<Boolean> callback, Handler handler) {
145        mAccounts.remove(getAccountHolder(account));
146        postAsyncAccountChangedEvent();
147        return runTask(mExecutor,
148                new AccountManagerTask<Boolean>(handler, callback, new Callable<Boolean>() {
149                    @Override
150                    public Boolean call() throws Exception {
151                        // Removal always successful.
152                        return true;
153                    }
154                }));
155    }
156 
157    @Override
158    public String getPassword(Account account) {
159        return getAccountHolder(account).getPassword();
160    }
161 
162    @Override
163    public void setPassword(Account account, String password) {
164        mAccounts.add(getAccountHolder(account).withPassword(password));
165    }
166 
167    @Override
168    public void clearPassword(Account account) {
169        setPassword(account, null);
170    }
171 
172    @Override
173    public AccountManagerFuture<Bundle> confirmCredentials(Account account, Bundle bundle,
174            Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
175        String password = bundle.getString(AccountManager.KEY_PASSWORD);
176        if (password == null) {
177            throw new IllegalArgumentException("Password is null");
178        }
179        final AccountHolder accountHolder = getAccountHolder(account);
180        final boolean correctPassword = password.equals(accountHolder.getPassword());
181        return runTask(mExecutor,
182                new AccountManagerTask<Bundle>(handler, callback, new Callable<Bundle>() {
183            @Override
184            public Bundle call() throws Exception {
185                Bundle result = new Bundle();
186                result.putString(AccountManager.KEY_ACCOUNT_NAME, accountHolder.getAccount().name);
187                result.putString(
188                        AccountManager.KEY_ACCOUNT_TYPE, AccountManagerHelper.GOOGLE_ACCOUNT_TYPE);
189                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, correctPassword);
190                return result;
191            }
192        }));
193    }
194 
195    @Override
196    public String blockingGetAuthToken(Account account, String authTokenType,
197            boolean notifyAuthFailure)
198            throws OperationCanceledException, IOException, AuthenticatorException {
199        AccountHolder accountHolder = getAccountHolder(account);
200        if (accountHolder.hasBeenAccepted(authTokenType)) {
201            // If account has already been accepted we can just return the auth token.
202            return internalGenerateAndStoreAuthToken(accountHolder, authTokenType);
203        }
204        AccountAuthTokenPreparation prepared = getPreparedPermission(account, authTokenType);
205        Intent intent = newGrantCredentialsPermissionIntent(false, account, authTokenType);
206        waitForActivity(mContext, intent);
207        applyPreparedPermission(prepared);
208        return internalGenerateAndStoreAuthToken(accountHolder, authTokenType);
209    }
210 
211    @Override
212    public AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType,
213            Bundle options, Activity activity, AccountManagerCallback<Bundle> callback,
214            Handler handler) {
215        return getAuthTokenFuture(account, authTokenType, activity, callback, handler);
216    }
217 
218    @Override
219    public AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType,
220            boolean notifyAuthFailure, AccountManagerCallback<Bundle> callback, Handler handler) {
221        return getAuthTokenFuture(account, authTokenType, null, callback, handler);
222    }
223 
224    private AccountManagerFuture<Bundle> getAuthTokenFuture(Account account, String authTokenType,
225            Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
226        final AccountHolder ah = getAccountHolder(account);
227        if (ah.hasBeenAccepted(authTokenType)) {
228            final String authToken = internalGenerateAndStoreAuthToken(ah, authTokenType);
229            return runTask(mExecutor,
230                    new AccountManagerAuthTokenTask(activity, handler, callback,
231                            account, authTokenType,
232                            new Callable<Bundle>() {
233                        @Override
234                        public Bundle call() throws Exception {
235                            return getAuthTokenBundle(ah.getAccount(), authToken);
236                        }
237                    }));
238        } else {
239            Log.d(TAG, "getAuthTokenFuture: Account " + ah.getAccount() +
240                    " is asking for permission for " + authTokenType);
241            final Intent intent = newGrantCredentialsPermissionIntent(
242                    activity != null, account, authTokenType);
243            return runTask(mExecutor,
244                    new AccountManagerAuthTokenTask(activity, handler, callback,
245                            account, authTokenType,
246                            new Callable<Bundle>() {
247                        @Override
248                        public Bundle call() throws Exception {
249                            Bundle result = new Bundle();
250                            result.putParcelable(AccountManager.KEY_INTENT, intent);
251                            return result;
252                        }
253                    }));
254        }
255    }
256 
257    private static Bundle getAuthTokenBundle(Account account, String authToken) {
258        Bundle result = new Bundle();
259        result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
260        result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
261        result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
262        return result;
263    }
264 
265    private String internalGenerateAndStoreAuthToken(AccountHolder ah, String authTokenType) {
266        synchronized (mAccounts) {
267            // Some tests register auth tokens with value null, and those should be preserved.
268            if (!ah.hasAuthTokenRegistered(authTokenType) &&
269                    ah.getAuthToken(authTokenType) == null) {
270                // No authtoken registered. Need to create one.
271                String authToken = UUID.randomUUID().toString();
272                Log.d(TAG, "Created new auth token for " + ah.getAccount() +
273                        ": autTokenType = " + authTokenType + ", authToken = " + authToken);
274                ah = ah.withAuthToken(authTokenType, authToken);
275                mAccounts.add(ah);
276            }
277        }
278        return ah.getAuthToken(authTokenType);
279    }
280 
281    @Override
282    public String peekAuthToken(Account account, String authTokenType) {
283        return getAccountHolder(account).getAuthToken(authTokenType);
284    }
285 
286    @Override
287    public void invalidateAuthToken(String accountType, String authToken) {
288        if(!AccountManagerHelper.GOOGLE_ACCOUNT_TYPE.equals(accountType)) {
289            throw new IllegalArgumentException("Invalid account type: " + accountType);
290        }
291        if (authToken == null) {
292            throw new IllegalArgumentException("AuthToken can not be null");
293        }
294        for (AccountHolder ah : mAccounts) {
295            if (ah.removeAuthToken(authToken)) {
296                break;
297            }
298        }
299    }
300 
301    @Override
302    public AuthenticatorDescription[] getAuthenticatorTypes() {
303        AuthenticatorDescription googleAuthenticator = new AuthenticatorDescription(
304                AccountManagerHelper.GOOGLE_ACCOUNT_TYPE, "p1", 0, 0, 0, 0);
305 
306        return new AuthenticatorDescription[] { googleAuthenticator };
307    }
308 
309    public void prepareAllowAppPermission(Account account, String authTokenType) {
310        addPreparedAppPermission(new AccountAuthTokenPreparation(account, authTokenType, true));
311    }
312 
313    public void prepareDenyAppPermission(Account account, String authTokenType) {
314        addPreparedAppPermission(new AccountAuthTokenPreparation(account, authTokenType, false));
315    }
316 
317    private void addPreparedAppPermission(AccountAuthTokenPreparation accountAuthTokenPreparation) {
318        Log.d(TAG, "Adding " + accountAuthTokenPreparation);
319        mAccountPermissionPreparations.add(accountAuthTokenPreparation);
320    }
321 
322    private AccountAuthTokenPreparation getPreparedPermission(Account account,
323            String authTokenType) {
324        for (AccountAuthTokenPreparation accountPrep : mAccountPermissionPreparations) {
325            if (accountPrep.getAccount().equals(account) &&
326                    accountPrep.getAuthTokenType().equals(authTokenType)) {
327                return accountPrep;
328            }
329        }
330        return null;
331    }
332 
333    private void applyPreparedPermission(AccountAuthTokenPreparation prep) {
334        if (prep != null) {
335            Log.d(TAG, "Applying " + prep);
336            mAccountPermissionPreparations.remove(prep);
337            mAccounts.add(getAccountHolder(prep.getAccount()).withHasBeenAccepted(
338                    prep.getAuthTokenType(), prep.isAllowed()));
339        }
340    }
341 
342    private Intent newGrantCredentialsPermissionIntent(boolean hasActivity, Account account,
343            String authTokenType) {
344        Intent intent = new Intent();
345        intent.setComponent(new ComponentName(mTestContext,
346                MockGrantCredentialsPermissionActivity.class.getCanonicalName()));
347        intent.putExtra(MockGrantCredentialsPermissionActivity.ACCOUNT, account);
348        intent.putExtra(MockGrantCredentialsPermissionActivity.AUTH_TOKEN_TYPE, authTokenType);
349        if (!hasActivity) {
350            // No activity provided, so we help the caller by adding the new task flag
351            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
352        }
353        return intent;
354    }
355 
356    private AccountHolder getAccountHolder(Account account) {
357        if (account == null) {
358            throw new IllegalArgumentException("Account can not be null");
359        }
360        for (AccountHolder accountHolder : mAccounts) {
361            if (account.equals(accountHolder.getAccount())) {
362                return accountHolder;
363            }
364        }
365        throw new IllegalArgumentException("Can not find AccountHolder for account " + account);
366    }
367 
368    private static <T> AccountManagerFuture<T> runTask(Executor executorService,
369            AccountManagerTask<T> accountManagerBundleTask) {
370        executorService.execute(accountManagerBundleTask);
371        return accountManagerBundleTask;
372    }
373 
374    private class AccountManagerTask<T> extends FutureTask<T> implements AccountManagerFuture<T> {
375 
376        protected final Handler mHandler;
377 
378        protected final AccountManagerCallback<T> mCallback;
379 
380        protected final Callable<T> mCallable;
381 
382        public AccountManagerTask(Handler handler,
383                AccountManagerCallback<T> callback, Callable<T> callable) {
384            super(new Callable<T>() {
385                @Override
386                public T call() throws Exception {
387                    throw new IllegalStateException("this should never be called, "
388                            + "but call must be overridden.");
389                }
390            });
391            mHandler = handler;
392            mCallback = callback;
393            mCallable = callable;
394        }
395 
396        private T internalGetResult(long timeout, TimeUnit unit)
397                throws OperationCanceledException, IOException, AuthenticatorException {
398            try {
399                if (timeout == -1) {
400                    return get();
401                } else {
402                    return get(timeout, unit);
403                }
404            } catch (CancellationException e) {
405                throw new OperationCanceledException();
406            } catch (TimeoutException e) {
407                // Fall through and cancel.
408            } catch (InterruptedException e) {
409                // Fall through and cancel.
410            } catch (ExecutionException e) {
411                final Throwable cause = e.getCause();
412                if (cause instanceof IOException) {
413                    throw (IOException) cause;
414                } else if (cause instanceof UnsupportedOperationException) {
415                    throw new AuthenticatorException(cause);
416                } else if (cause instanceof AuthenticatorException) {
417                    throw (AuthenticatorException) cause;
418                } else if (cause instanceof RuntimeException) {
419                    throw (RuntimeException) cause;
420                } else if (cause instanceof Error) {
421                    throw (Error) cause;
422                } else {
423                    throw new IllegalStateException(cause);
424                }
425            } finally {
426                cancel(true /* Interrupt if running. */);
427            }
428            throw new OperationCanceledException();
429        }
430 
431        @Override
432        public T getResult()
433                throws OperationCanceledException, IOException, AuthenticatorException {
434            return internalGetResult(-1, null);
435        }
436 
437        @Override
438        public T getResult(long timeout, TimeUnit unit)
439                throws OperationCanceledException, IOException, AuthenticatorException {
440            return internalGetResult(timeout, unit);
441        }
442 
443        @Override
444        public void run() {
445            try {
446                set(mCallable.call());
447            } catch (Exception e) {
448                setException(e);
449            }
450        }
451 
452        @Override
453        protected void done() {
454            if (mCallback != null) {
455                postToHandler(getHandler(), mCallback, this);
456            }
457        }
458 
459        protected Handler getHandler() {
460            return mHandler == null ? mMainHandler : mHandler;
461        }
462 
463    }
464 
465    private static <T> void postToHandler(Handler handler, final AccountManagerCallback<T> callback,
466            final AccountManagerFuture<T> future) {
467        handler.post(new Runnable() {
468            @Override
469            public void run() {
470                callback.run(future);
471            }
472        });
473    }
474 
475    private class AccountManagerAuthTokenTask extends AccountManagerTask<Bundle> {
476 
477        private final Activity mActivity;
478 
479        private final AccountAuthTokenPreparation mAccountAuthTokenPreparation;
480 
481        private final Account mAccount;
482 
483        private final String mAuthTokenType;
484 
485        public AccountManagerAuthTokenTask(Activity activity, Handler handler,
486                AccountManagerCallback<Bundle> callback,
487                Account account, String authTokenType,
488                Callable<Bundle> callable) {
489            super(handler, callback, callable);
490            mActivity = activity;
491            mAccountAuthTokenPreparation = getPreparedPermission(account, authTokenType);
492            mAccount = account;
493            mAuthTokenType = authTokenType;
494        }
495 
496        @Override
497        public void run() {
498            try {
499                Bundle bundle = mCallable.call();
500                Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
501                if (intent != null) {
502                    // Start the intent activity and wait for it to finish.
503                    if (mActivity != null) {
504                        waitForActivity(mActivity, intent);
505                    } else {
506                        waitForActivity(mContext, intent);
507                    }
508                    if (mAccountAuthTokenPreparation == null) {
509                        throw new IllegalStateException("No account preparation ready for " +
510                                mAccount + ", authTokenType = " + mAuthTokenType +
511                                ". Add a call to either prepareGrantAppPermission(...) or " +
512                                "prepareRevokeAppPermission(...) in your test before asking for " +
513                                "an auth token");
514                    } else {
515                        // We have shown the Allow/Deny activity, and it has gone away. We can now
516                        // apply the pre-stored permission.
517                        applyPreparedPermission(mAccountAuthTokenPreparation);
518                        generateResult(getAccountHolder(mAccount), mAuthTokenType);
519                    }
520                } else {
521                    set(bundle);
522                }
523            } catch (Exception e) {
524                setException(e);
525            }
526        }
527 
528        private void generateResult(AccountHolder accountHolder, String authTokenType)
529                throws OperationCanceledException {
530            if (accountHolder.hasBeenAccepted(authTokenType)) {
531                String authToken = internalGenerateAndStoreAuthToken(accountHolder, authTokenType);
532                // Return a valid auth token.
533                set(getAuthTokenBundle(accountHolder.getAccount(), authToken));
534            } else {
535                // Throw same exception as when user clicks "Deny".
536                throw new OperationCanceledException("User denied request");
537            }
538        }
539    }
540 
541    /**
542     * This method starts {@link MockGrantCredentialsPermissionActivity} and waits for it
543     * to be started before it returns.
544     *
545     * @param context the context to start the intent in
546     * @param intent the intent to use to start MockGrantCredentialsPermissionActivity
547     */
548    private void waitForActivity(Context context, Intent intent) {
549        final Object mutex = new Object();
550        BroadcastReceiver receiver = new BroadcastReceiver() {
551            @Override
552            public void onReceive(Context context, Intent intent) {
553                synchronized (mutex) {
554                    mutex.notifyAll();
555                }
556            }
557        };
558        if (!MockGrantCredentialsPermissionActivity.class.getCanonicalName().
559                equals(intent.getComponent().getClassName())) {
560            throw new IllegalArgumentException("Can only wait for "
561                    + "MockGrantCredentialsPermissionActivity");
562        }
563        mContext.registerReceiver(receiver, new IntentFilter(MUTEX_WAIT_ACTION));
564        context.startActivity(intent);
565        try {
566            Log.d(TAG, "Waiting for broadcast of " + MUTEX_WAIT_ACTION);
567            synchronized (mutex) {
568                mutex.wait(WAIT_TIME_FOR_GRANT_BROADCAST_MS);
569            }
570        } catch (InterruptedException e) {
571            throw new IllegalStateException("Got unexpected InterruptedException");
572        }
573        Log.d(TAG, "Got broadcast of " + MUTEX_WAIT_ACTION);
574        mContext.unregisterReceiver(receiver);
575    }
576 
577    private void postAsyncAccountChangedEvent() {
578        // Mimic that this does not happen on the main thread.
579        new AsyncTask<Void, Void, Void>() {
580            @Override
581            protected Void doInBackground(Void... params) {
582                mContext.sendBroadcast(new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION));
583                return null;
584            }
585        }.execute();
586    }
587 
588    /**
589     * Internal class for storage of prepared account auth token permissions.
590     *
591     * This is used internally by {@link MockAccountManager} to mock the same behavior as clicking
592     * Allow/Deny in the Android {@link GrantCredentialsPermissionActivity}.
593     */
594    private static class AccountAuthTokenPreparation {
595 
596        private final Account mAccount;
597 
598        private final String mAuthTokenType;
599 
600        private final boolean mAllowed;
601 
602        private AccountAuthTokenPreparation(Account account, String authTokenType,
603                boolean allowed) {
604            mAccount = account;
605            mAuthTokenType = authTokenType;
606            mAllowed = allowed;
607        }
608 
609        public Account getAccount() {
610            return mAccount;
611        }
612 
613        public String getAuthTokenType() {
614            return mAuthTokenType;
615        }
616 
617        public boolean isAllowed() {
618            return mAllowed;
619        }
620 
621        @Override
622        public String toString() {
623            return "AccountAuthTokenPreparation{" +
624                    "mAccount=" + mAccount +
625                    ", mAuthTokenType='" + mAuthTokenType + '\'' +
626                    ", mAllowed=" + mAllowed +
627                    '}';
628        }
629    }
630}

[all classes][org.chromium.sync.test.util]
EMMA 2.0.5312 (C) Vladimir Roubtsov