1

I am trying to get the Authentication Token from an account in Android before I make my request to the server. I Am trying to control the flow with a CountdownLatch so that it waits until:

  • a) A timeout (10s)
  • b) We get the token

    private CountDownLatch tokenLatch = new CountDownLatch(1);
    final long tokenTimeoutSeconds = 10;
    AccountManager manager = AccountManager.get(mContext);
    Account userAccount = getCurrentAccount();
    // Get the auth token
    if (userAccount != null) {
        AccountManagerFuture<Bundle> future = manager.getAuthToken(userAccount, AccountUtility.AUTHTOKEN_TYPE_FULL_ACCESS, true, new AccountManagerCallback<Bundle>() {
            @Override
            public void run(AccountManagerFuture<Bundle> future) {
                try {
                    Bundle bundle = future.getResult();
                    currentAuthToken = bundle.get(AccountManager.KEY_AUTHTOKEN).toString();
                    tokenLatch.countDown();
                } catch (Exception e) {
                    Log.e(LOG_TAG, "Problem getting auth token!", e);
                }
            }
        }, null);
    
        try {
            tokenLatch.await(tokenTimeoutSeconds, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Log.e(LOG_TAG, "Interupted while getting auth token!", e);
        }
    

Context is passed:

mContext = ...  getApplicationContext();

Right now it exits before either of those two cases. It does, however, always reach the AccountManagerCallback after all other processes are finished. Strange. I am most definitely doing something wrong. Thanks for the helperooni!

austin_ce
  • 1,063
  • 15
  • 28
  • Do I understand correctly that: 1) execution passes through await() before 10 seconds have elapsed, and also without having received the auth token, and 2) you are not seeing the "Interrupted while..." log output? If so, it sounds like tokenLatch was zero when you starting the processing. In your posted code you show tokenLatch defined and initialized at package level. Why not initialize it to 1 just before starting the processing so you can be certain it has the right value? – Bob Snyder Jun 25 '15 at 03:49
  • Yes 1) is correct, although it doesn't pass when I make the timeout infinite, so it must not be the problem. 2) No I am not, while running the debugger I see that the state of the Latch is 1 and the breakpoint on the InterruptedException logic is never hit, although it seems like AccountManager never gets the Auth Token at all.. unless it is broken and then returns once all other processes are finished. **I am confused why the callback function is never reached when Latched like this.** – austin_ce Jun 25 '15 at 04:28
  • I don't think this is the cause of the problem you are observing, but the [documentation](http://developer.android.com/reference/android/accounts/AccountManager.html) for getAuthToken() states that if no authToken is cached, and there is no saved password, the result bundle contains only an Intent to prompt the user to enter the password. Your code expects the bundle to always contain the authToken. At some point you will need to fix that. – Bob Snyder Jun 25 '15 at 05:31
  • Posting an answer soon. – Bob Snyder Jun 25 '15 at 05:39

1 Answers1

2

This explanation presumes the posted code is running on the main thread. Because the Handler parameter in the call to getAuthToken() is null, the callback will also run on the main thread. This is a deadlock situation. After calling getAuthToken() the main thread blocks on the latch await(). The callback cannot run because the main thread is blocked. The latch never counts down to zero because the callback can't run.

The code posted at this blog offers an example of how an auth token can be obtained on the main thread without blocking.

Bob Snyder
  • 37,759
  • 6
  • 111
  • 158