Im having problem on getAuthToken() provided in android AccountManager where (steps as below):
after resetting the authToken to null (in logout process)
invalidate the new null authToken
set new authToken to new string provided by server (login back)
invalidate the new string authToken
and try to check/get on the new authToken,
but on getting the new authToken via getAuthToken() method, the call
future.getResult() hangs forever. this doesnt happen on first time login *during account creation. i able to get the auth token using the same callable class.
Below are my defined callable class. please advice on how to solve this matter.
private AccountManagerFuture<Bundle> future = null;
private String authToken;
class GetAuthTokenTask implements Callable<Bundle> {
private AccountManager accountManager;
private Account account;
private String authType;
private Activity activity;
public GetAuthTokenTask(AccountManager accountManager, Account account, String authType, Activity activity) {
this.accountManager = accountManager;
this.account = account;
this.authType = authType;
this.activity = activity;
}
/**
* Computes a result, or throws an exception if unable to do so.
*
* #return computed result
* #throws Exception if unable to compute a result
*/
#Override
public Bundle call() throws Exception {
return getAuthToken();
}
private Bundle getAuthToken() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
future = accountManager.getAuthToken(account, authType, null, activity, null, null);
try {
Bundle result = future.getResult();
if (result!=null) {
authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
}
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
}
Bundle output = new Bundle();
output.putString(AccountManager.KEY_AUTHTOKEN, authToken );
return output;
}
}
//caller method
private String getAuthToken(Account account, String authType) {
ExecutorService es = Executors.newSingleThreadExecutor();
GetAuthTokenTask authTokenTask = new GetAuthTokenTask(accountManager, account, authType, (Activity)getBaseContext());
FutureTask<Bundle> result = new FutureTask<Bundle>(authTokenTask);
es.execute(result);
Bundle resultBundle = new Bundle();
try {
resultBundle = result.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return resultBundle.getString(AccountManager.KEY_AUTHTOKEN);
}
try to avoid use of activity like this
future = accountManager.getAuthToken(account, authType, null, true, null, null);
Related
Calling AccountManager.getAuthToken() with authTokenType = "lh2" for Picasa Web Service now does not return on Android 5.x; getAuthToken with "lh2" still works fine with Android 4.4.x and 6.x, just not 5.x. This was all working fine for Lollipop up until sometime August-September 2016 and nothing changed in the code or app in that time.
Anyone else experiencing this problem with getAuthToken for "lh2" on Lollipop devices? Is there another way to get the account auth token to pass to PicasaWebService?
Here is the relevant code to getAuthToken:
//...
String accountName = "someone#somedamain.com"
Account selectedAccount = null;
AccountManager accountManager = (AccountManager)activity.getSystemService(Context.ACCOUNT_SERVICE);
Account[] list = accountManager.getAccounts();
for (android.accounts.Account a:list) {
if (a.name.equals(accountName)) {
selectedAccount = a;
break;
}
}
accountManager.invalidateAuthToken("com.google", null);
AccountManagerFuture<Bundle> tokenFuture = getAccountManager().getAuthToken(
selectedAccount,
"lh2",
null,
activity,
new OnTokenAcquired(),
new Handler(new OnTokenError()));
//...
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
#Override
public void run(AccountManagerFuture<Bundle> result) {
try {
Bundle b = result.getResult();
if (b.containsKey(AccountManager.KEY_INTENT)) {
Intent intent = b.getParcelable(AccountManager.KEY_INTENT);
int flags = intent.getFlags();
flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK;
intent.setFlags(flags);
activity.startActivityForResult(intent, REQUEST_AUTHENTICATE);
return;
}
if (b.containsKey(AccountManager.KEY_AUTHTOKEN)) {
String authToken = b.getString(AccountManager.KEY_AUTHTOKEN);
// set authtoken to Picasa Web Service
_picasaService = new PicasawebService("myApp");
_picasaService.setUserToken(authToken);
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private class OnTokenError implements Handler.Callback {
#Override
public boolean handleMessage(Message msg) {
Log.e("onError","ERROR");
return false;
}
}
//...
Thanks!
It seems like the authTokenType should be the "OAuth scopes" as found on https://developers.google.com/oauthplayground/ prefixed with "oauth2:".
For Picasa Web, this is https://picasaweb.google.com/data/
accountManager.getAuthToken(account, "oauth2:https://picasaweb.google.com/data/",
options, false, new GetAuthTokenCallback(), null);
I want to connect with oAuth2 the IMAP of Google. But the App seems to be not really authorize.
I try using the scope oauth2:https://mail.google.com/ but the token returned is unusable by the server.
Then I want to set more datas on the scope Android say : "INVALID_SCOPE".
The scope is:
String scope = String.format("oauth2:server:%s:client_id:%s:api_scope:%s",
API_KEY, CLIENT_ID, "https://mail.google.com/");
With:
API KEY: the API KEY of the Server Application (not the web Application Client Api Key)
CLIENT_ID: The Client ID of the Service Account (not the web application)
The full Code is :
public static String getToken(final Context context, final Activity activity, final String accountName)
{
AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>()
{
#Override
protected String doInBackground(Void... params)
{
String token = null;
try
{
String scope = String.format("oauth2:server:%s:client_id:%s:api_scope:%s", API_KEY, CLIENT_ID, "https://mail.google.com/");
Log.i("GooglePlusSignupTool", "scope : "+scope);
token = GoogleAuthUtil.getToken(context, accountName, scope);
}
catch (IOException transientEx)
{
// Network or server error, try later
Log.e("GoogleSignupTool", transientEx.toString());
}
catch (UserRecoverableAuthException e)
{
// Recover (with e.getIntent())
Log.e("GoogleSignupTool", e.toString());
Intent recover = e.getIntent();
context.startActivity(recover);//ForResult(recover, REQUEST_CODE_TOKEN_AUTH);
}
catch (GoogleAuthException authEx)
{
// The call is not ever expected to succeed
// assuming you have already verified that
// Google Play services is installed.
Log.e("GoogleSignupTool", authEx.toString());
}
return token;
}
#Override
protected void onPostExecute(String token)
{
Log.i("GoogleSignupTool", "Access token retrieved:" + token);
}
};
task.execute();
Log.i("GoogleSignupTool", "END");
return "";
}
I have the below implementation in my home activity to check if an authtoken exists and if not redirect to the login activity. It gets the job done however there is an issue...
Scenario
I launch the app for the first time (authtoken obviously null),the confirmLogin method (which is called always gets fired,finds no authtoken and displays the login...but there is like a 5-15 second delay in doing this in which time the user is in the homepage and back to login page again. How can I instictively make sure thet the homepage is not viewed as the method checks for the authtoken?
private void ConfirmLogin(String accountType, String authTokenType) {
#SuppressWarnings("unused")
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthTokenByFeatures(accountType, authTokenType, null, this, null, null,
new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> future) {
Bundle bnd = null;
try {
bnd = future.getResult();
authToken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
if (authToken != null) {
String accountName = bnd.getString(AccountManager.KEY_ACCOUNT_NAME);
mConnectedAccount = new Account(accountName, AccountGeneral.ACCOUNT_TYPE);
}
else{
Intent intent = new Intent(AppHome.this, AppLogin.class);
intent.putExtra(AppLogin.ARG_ACCOUNT_TYPE, AccountGeneral.ACCOUNT_TYPE);
intent.putExtra(AppLogin.ARG_AUTH_TYPE, AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS);
intent.putExtra(AppLogin.ARG_ACCOUNT_NAME, AccountGeneral.ACCOUNT_NAME);
startActivityForResult(intent, REQ_LOGIN);
}
} catch (Exception e) {
finish();
Log.e("Account Login terminated", e+"");
}
}
}
, null);
}
I'm testing my app on a device and after doing a certain activity 5 or 6 times, the app crashes with error: java.lang.IllegalStateException: calling this from your main thread can lead to deadlock
The activity is simple: User puts in a task and clicks save. At this time, I get their username and authtoken and make a POST request to my server. I wonder if my app is crashing because each time I am getting the username and authtoken.
The crash is happening in the below code at line new CreateTask.
public boolean onOptionsItemSelected(MenuItem menuItem) {
if (menuItem.getTitle().toString().equalsIgnoreCase("save")) {
new CreateTask(this,taskName.getText().toString(), getMyAccount().getUsername(), getMyAccount().getAuthToken()).execute();
return true;
}
return true
}
The code for getMyAccount() looks like this:
private MyAccount getMyAccount() {
MyAccount myAccount = new MyAccount(getApplicationContext());
return myAccount;
}
And finally the class MyAccount looks like below:
public class MyAccount {
private static final String TAG = "MyAccount";
private final Account account;
private final AccountManager manager;
private final Context context;
public MyAccount(final Context context) {
this.context = context;
final Account[] accounts = AccountManager.get(context).getAccountsByType("com.myapp");
account = accounts.length > 0 ? accounts[0] : null;
manager = AccountManager.get(context);
}
public String getPassword() {
return account!=null? manager.getPassword(account): null;
}
public String getUsername() {
return account!=null? account.name: null;
}
public String getAuthToken() {
if (account != null) {
AccountManagerFuture<Bundle> future = manager.getAuthToken(account,"com.myapp", false, null, null);
try {
Bundle result = future.getResult();
return result != null ? result.getString(KEY_AUTHTOKEN) : null;
} catch (AccountsException e) {
Log.e(TAG, "Auth token lookup failed", e);
return null;
} catch (IOException e) {
Log.e(TAG, "Auth token lookup failed", e);
return null;
}
}
else
return null;
}
}
Question
Is this not a best practice? Is my app crashing because I keep getting the account and authtoken repeatedly? What is a way to avoid this?
Please note this statement in the description for getAuthToken method you are using:
This method may be called from any thread, but the returned
AccountManagerFuture must not be used on the main thread.
You obviously violate this, as your MyAccount#getAuthToken() is accessing the Future on the main thread : Bundle result = future.getResult();
I assume that is why you get the exception.
Hope that helps.
Thread CrearEventoHilo = new Thread(){
public void run(){
//do something that retrun "Calling this from your main thread can lead to deadlock"
}
};
CrearEventoHilo.start();
CrearEventoHilo.interrupt();
I have a problem with google account OAuth 2 tokens.
We need token for access account information (numeric id, email, user name)
After request getAuthToken(account, SCOPE, options, mContext, getAuthTokenCallback, null) in AccountManager, token is not available for access to account information.
Response of HTTP request https://www.googleapis.com/plus/v1/people/me (header "Authorization: OAuth ya29.AHES6ZSuMvL3FoxqXfevYevWyEmTPOE1HXW7_Tj6l3UAN-2J7kTs0-I")
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit Exceeded. Please sign up",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit Exceeded. Please sign up"
}
}
Why this error hapens?
Previously works with two types AuthSub tokens separated by spaces.(SCOPE_OLD_PERMITIONS)
Now it not works & causes java.io.IOException
How can I get valid token?
This is request for get token:
TCGoogleAccountsManager mng = new TCGoogleAccountsManager(this);
mng.requestAccountOAuthToken(this, acc);
сlass that helps get token:
public class TCGoogleAccountsManager {
private static final String CLIENT_SECRET = ...;
private static final String CLIENT_ID = ...;
private static final String SCOPE_CONTACTS_API = "cp";
private static final String SCOPE_ANDROID_API = "android";
private static final String SCOPE_GOOGPE_PLUS = "oauth2:https://www.googleapis.com/auth/plus.me";
private static final String SCOPE_MY_INFO = "oauth2:https://www.googleapis.com/auth/userinfo.email";
private static final String SCOPE_OLD_PERMITIONS = "oauth2:https://www-opensocial.googleusercontent.com/api/people/ oauth2:https://www.googleapis.com/auth/userinfo#email";
private static final String SCOPE = SCOPE_GOOGPE_PLUS;
private static final String COM_GOOGLE = "com.google";
private AccountManager mManager;
private OnGetOAuthTokenRequestCompletedListener mTokenRequestListener;
public TCGoogleAccountsManager(Context ctx) {
mManager = AccountManager.get(ctx.getApplicationContext());
mTokenRequestListener = new GoogleTokenListener(
ctx.getApplicationContext());
}
public int getAccountsNumber() {
return mManager.getAccountsByType(COM_GOOGLE).length;
}
public Account[] getGoogleAccounts() {
return mManager.getAccountsByType(COM_GOOGLE);
}
public Account getGoogleAccountByName(String name) {
Account foundAcc = null;
if (name != null && !name.equals("")) {
Account[] googleAccounts = mManager.getAccountsByType(COM_GOOGLE);
for (int i = 0; i < googleAccounts.length; i++) {
if (name.equals(googleAccounts[i].name)) {
foundAcc = googleAccounts[i];
break;
}
}
}
return foundAcc;
}
public Account getGoogleAccount(int index) {
return getGoogleAccounts()[index];
}
public void requestAccountOAuthToken(Activity mContext, Account account) {
try {
final Bundle options = new Bundle();
options.putString("client_id", CLIENT_ID);
options.putString("client_secret", CLIENT_SECRET);
mManager.getAuthToken(account, SCOPE, options, mContext,
getAuthTokenCallback, null);
} catch (Exception e) {
e.printStackTrace();
}
}
private AccountManagerCallback<Bundle> getAuthTokenCallback = new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
try {
final Bundle result = future.getResult();
final String accountName = result
.getString(AccountManager.KEY_ACCOUNT_NAME);
final String authToken = result
.getString(AccountManager.KEY_AUTHTOKEN);
boolean success = (accountName != null && authToken != null);
if (!success) {
if (mTokenRequestListener != null) {
mTokenRequestListener.onRequestCompleted(false,
accountName, authToken);
}
} else {
// refresh token. We need fresh token.
mManager.invalidateAuthToken(COM_GOOGLE, authToken);
mManager.getAuthToken(getGoogleAccountByName(accountName),
SCOPE, false, getAuthTokenCallbackInvalidated, null);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
private AccountManagerCallback<Bundle> getAuthTokenCallbackInvalidated = new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
try {
final Bundle result = future.getResult();
final String accountName = result
.getString(AccountManager.KEY_ACCOUNT_NAME);
final String authToken = result
.getString(AccountManager.KEY_AUTHTOKEN);
boolean success = (accountName != null && authToken != null);
if (mTokenRequestListener != null)
mTokenRequestListener.onRequestCompleted(success,
accountName, authToken);
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
Thanks and Regards.
You need a client_id to use with OAuth2, which you can obtain by registering your application at https://code.google.com/apis/console