androidretrofit2accountmanager

AccountManager.blockingGetAuthToken stucks


I created a AbstractThreadedSyncAdapter that synchronizes data with the server through Retrofit2. To handle authentication I created a interceptor to add the token.

When the interceptor needs to refresh the token by calling accountManager.blockingGetAuthToken(...) it stucks until a android.accounts.OperationCanceledException is thrown. When I put a breakpoint in the AbstractAccountAuthenticator -> getAuthToken it is not hit until the interceptor is finished.

public class TokenInterceptor implements Interceptor {

  @Override
  public Response intercept(Chain chain) throws IOException {
    String oldToken = accountManager.peekAuthToken(accounts[0], LoginActivity.AUTH_TOKEN_TYPE);
    
    if(newTokenRequired){
       accountManager.invalidateAuthToken(LoginActivity.ACCOUNT_TYPE, oldToken);
    }
    
    String token = accountManager.blockingGetAuthToken(accounts[0], LoginActivity.AUTH_TOKEN_TYPE, true); // <-- stucks here
    
    // add token to request and return response
  }

}

This is the getAuthToken method in the AccountAuthenticator:

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
   final AccountManager accountManager = AccountManager.get(context);
   final String refreshToken = accountManager.getPassword(account);
   final Bundle bundle = new Bundle();

   if (refreshToken != null) {
       try {
           Response<RefreshTokenResponse> refreshTokenResponse = apiService.refreshToken(refreshToken).execute();

           if (refreshTokenResponse.isSuccessful()) {
               bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
               bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
               bundle.putString(AccountManager.KEY_AUTHTOKEN, refreshTokenResponse.body().getAccess_Token());
               accountManager.setPassword(account, refreshTokenResponse.body().getRefresh_token());
               return bundle;
           }
       } catch (Exception e) {
           Log.e(AccountAuthenticator.class.getName(), "getAuthToken: failed to get access token", e);
       }
   }

   // start login intent
   final Intent intent = new Intent(context, LoginActivity.class);
   intent.putExtra(LoginActivity.PARAM_USER_PASS, account.name);
   intent.putExtra(LoginActivity.AUTH_TOKEN_TYPE, authTokenType);
   intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
   bundle.putParcelable(AccountManager.KEY_INTENT, intent);
   return bundle;
}

I don't think it is not an issue how blockingGetAuthToken is called because in case I comment out the invalidateAuthToken(...) in the interceptor it returns a cached token.

Could there be an issue as I'm making another HTTP request in the interceptor? (apiService.getBlaBlaBla() -> tokenInterceptor -> getAuthToken -> apiService.refreshToken() )


Solution

  • The problem was that the breakpoint was actually hit but in a different Frame. This way the breakpoint blocked the authenticator and the interceptor timed out.

    To switch between frames go to Debug -> Debugger -> Frames and select the appropriate Frame from the drop down.

    Here more info: https://developer.android.com/studio/debug/index.html#stackFrames