"lead to deadlock" error when doing a certain activity multiple times - android

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();

Related

Android AccountManager getAuthToken for lh2 scope does not callback for Lollipop

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);

Listener not called in Unit Test

I tried to create unit test for my project.
Inside my unit test class I have two methods testCalculate() and testLogin(). Method testCalculate() running fine, meaning that test passed and I got correct testing result.
But problem is in testLogin(), I expect that my code will be print something inside the listener, but it never printed.Meaning that I never get this line
System.out.println("Login success ======= " + response.getResponseObject());
My login method that I want to test itself running fine, meaning if I use it inside my real app, it will login successfully and return some datas that I got from server.
Kindly advise what is possible cause that make my listener is not working in unit test. Really appreciate for any kind help.
#RunWith(RobolectricTestRunner.class)
#Config(manifest = Config.NONE)
public class LoginManagerTest {
private static final String TAG = LoginManagerTest.class.getCanonicalName();
private String usernameStr = "androidtest#gmail.com";
private String passwordStr = "********";
private LoginManager loginManager;
#Test
public void testLogin() throws Exception {
ResponseHandlerListener<String> listener = new ResponseHandlerListener<String>() {
#Override
public void onReceive(AxonResponse<String> response) {
System.out.println("Login success ======= " + response.getResponseObject());
String loginSuccess = Constants.LOGIN_SUCCESS;
assertEquals(TAG, loginSuccess, response.getResponseObject());
}
};
Context context = Robolectric.getShadowApplication().getApplicationContext();
loginManager = new LoginManager(context);
loginManager.login(usernameStr, passwordStr, null, listener);
}
#Test
public void testCalculate() throws Exception {
Context context = Robolectric.getShadowApplication().getApplicationContext();
loginManager = new LoginManager(context);
int num = 5;
int sum = loginManager.calculate(num);
System.out.println("sum = " + sum);
assertEquals(10, sum);
}
}
I had the same issue. I solved by using Robolectric.flushForegroundThreadScheduler(). This will run queued tasks that is on forground thread.
It's going to be like this:
#Test
public void testLogin() throws Exception {
ResponseHandlerListener<String> listener = new ResponseHandlerListener<String>() {
#Override
public void onReceive(AxonResponse<String> response) {
System.out.println("Login success ======= " + response.getResponseObject());
String loginSuccess = Constants.LOGIN_SUCCESS;
assertEquals(TAG, loginSuccess, response.getResponseObject());
}
};
Context context = Robolectric.getShadowApplication().getApplicationContext();
loginManager = new LoginManager(context);
loginManager.login(usernameStr, passwordStr, null, listener);
ThreadSleep(500); //Wait for login process to be executed
Robolectric.flushForegroundThreadScheduler(); // this will run all the pending queue on Main Thread
}
I think the problem is the callback will be called after time, it means run asynchronus, and the test is out of time with other (main) thread. I have no experience about Robolectric test, but a little bit about AndroidTestSuite. So, I suggest you need some waitting time before the listener callback is called. Here is the sample and the other idea. Hope that help.
Update:
Try the below idea (Not tested):
private Object lock = new Object();
#Test
public void testLogin() throws Exception {
ResponseHandlerListener<String> listener = new ResponseHandlerListener<String>() {
#Override
public void onReceive(AxonResponse<String> response) {
System.out.println("Login success ======= " + response.getResponseObject());
String loginSuccess = Constants.LOGIN_SUCCESS;
assertEquals(TAG, loginSuccess, response.getResponseObject());
synchronized (lock) {
lock.notifyAll(); // Will wake up lock.wait()
}
}
};
synchronized (lock) {
Context context = Robolectric.getShadowApplication().getApplicationContext();
loginManager = new LoginManager(context);
loginManager.login(usernameStr, passwordStr, null, listener);
lock.wait();
}
}

Understanding authentication flow in OneDrive with Android and Rest API

I tried implementing a signIn method from the OneDrive API, but I am not sure I correctly understood the workflow.
Basically, on first launch of the app, I want to have both the login window and the "authorise the app to..." window". But then, when the user launches the app again, I would like to be directly connected to the app, without any window.
Instead, with the following code, I keep having the second window (where the user decides to accept the app)
#Override
public void signIn() {
//personal code
linkingStarted = true;
signInStatus = SignInStatus.SIGNING_IN;
activity.setUpWait(R.layout.popup_waitgif_white);
//end of personal code
mAuthClient = AuthClientFactory.getAuthClient(activity.getApplication());
if (mAuthClient.getSession().isExpired() && Util.isConnectedToInternet(activity)) {
activity.alertOnUIThread("Login again");
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
mAuthClient.login(activity, SCOPES, mAuthListener);
}
});
} else if (!Util.isConnectedToInternet(activity)) {
activity.alertOnUIThread(activity.getString(R.string.alert_verifyconnection));
} else {
activity.alertOnUIThread("Resigned In OneDrive");
signInStatus = SignInStatus.SIGNED_IN;
mAuthClient.initialize(SCOPES, new AuthListener() {
#Override
public void onAuthComplete(final AuthStatus status, final AuthSession session, final Object userState) {
if (status == AuthStatus.CONNECTED) {
authToken = session.getAccessToken();
oneDriveService = getOneDriveService();
signInStatus = SignInStatus.SIGNED_IN;
} else {
authenticationFailure();
Log.v(TAG, "Problem connecting");
}
}
#Override
public void onAuthError(final AuthException exception, final Object userState) {
//mAuthClient.login(activity, SCOPES, mAuthListener);
}
}, null, authToken);
}
}
and the AuthClientFactory is just this:
public class AuthClientFactory {
private static AuthClient authClient;
private static final String CLIENT_ID = "00000000XXXXX";
public static AuthClient getAuthClient(Context context) {
if (authClient == null)
authClient = new AuthClient(context, OneDriveOAuthConfig.getInstance(), CLIENT_ID);
return authClient;
}
}
You would have an easier time with the OneDrive SDK for Android, as authentication is a much simpler process.
final MSAAuthenticator msaAuthenticator = new MSAAuthenticator() {
#Override
public String getClientId() {
return "<msa-client-id>";
}
#Override
public String[] getScopes() {
return new String[] { "onedrive.appfolder", "wl.offline_access"};
}
}
final IClientConfig oneDriveConfig = new DefaultClientConfig.createWithAuthenticator(msaAuthenticator);
final IOneDriveClient oneDriveClient = new OneDriveClient
.Builder()
.fromConfig(oneDriveConfig)
.loginAndBuildClient(getActivity());
That will take care of the user authentication flow and then give you a service object that makes interacting with OneDrive straight-forward. See the full example application.

android getAuthToken hang after authToken reset

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);

AsyncTask in an android socket application

i am making an android socket app to communicate with the server for creating accounts, and i noticed i have to do this in AsyncTask sub class, even when i seperate it to another class without UI,but i am terribly confused how can i use AsyncTask on this, is there any one expert here who can help me please?
this is the code:
public class AccountCreator extends Activity {
public AccountCreator(){
super();
}
// for I/O
ObjectInputStream sInput; // to read from the socket
ObjectOutputStream sOutput; // to write on the socket
Socket socket;
public static String LOGTAG="Lifemate";
public String server = "localhost";
public String username = "user";
public String password = "rezapassword" ;
public int port = 1400;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(LOGTAG,"oncreate called");
this.start();
}
AccountCreator(String server, int port, String username,String password) {
this.server = "localhost";
this.port = 1400;
this.username = username;
Log.i(LOGTAG,"first accountcreator called");
}
public boolean start() {
// try to connect to the server
//this method returns a value of true or false when called
try {
socket = new Socket(server, port);
}
// if it failed not much I can so
catch(Exception ec) {
// display("Error connectiong to server:" + ec);
Log.i(LOGTAG,"Error connectiong to server:" + ec);
return false;
}
String msg = "Connection accepted " + socket.getInetAddress() + ":" +
socket.getPort();
// display(msg);
Log.i(LOGTAG, msg);
/* Creating both Data Stream */
try
{
sInput = new ObjectInputStream(socket.getInputStream());
sOutput = new ObjectOutputStream(socket.getOutputStream());
}
catch (IOException eIO) {
// display("Exception creating new Input/output Streams: " + eIO);
Log.i(LOGTAG,"Exception creating new Input/output Streams: " +
eIO);
return false;
}
// creates the Thread to listen from the server
// Send our username to the server this is the only message that we
// will send as a String. All other messages will be ChatMessage objects
try
{
sOutput.writeObject(username);
sOutput.writeObject(password);
}
catch (IOException eIO) {
// display("Exception doing login : " + eIO);
Log.i(LOGTAG,"Exception doing login : " + eIO);
disconnect();
return false;
}
// success we inform the caller that it worked
return true;
}
// private void display(String msg) {
// TextView screenprint = (TextView)findViewById(R.id.systemmessages);
// screenprint.setText(msg);
// }
private void disconnect() {
Log.i(LOGTAG,"reached disconnect");
try {
if(sInput != null) sInput.close();
}
catch(Exception e) {} // not much else I can do
try {
if(sOutput != null) sOutput.close();
}
catch(Exception e) {} // not much else I can do
try{
if(socket != null) socket.close();
}
catch(Exception e) {} // not much else I can do
}
public void Begin() {
Log.i(LOGTAG,"it begun");
int portNumber = 1400;
String serverAddress = server;
String userName = username;
String newpassword = password;
AccountCreator accountcreator = new AccountCreator(serverAddress, portNumber,
userName,password);
if(!accountcreator.start())
return;
}
}
i was trying to put whole code in Async, i dont know if was i right, do i need to do that also or just some parts of it?
In brief, AsyncTask contains a few methods which may be helpful:
onPreExecute:
This method is the first block of code executed when calling asyncTask.execute(); (runs on mainUIThread).
doInBackground:
Here you put all the code which may suspend you main UI (causes hang for your application) like internet requests, or any processing which may take a lot of memory and processing. (runs on background thread), contains one parameter taken from the asyncTask.execute(ParameterType parameter);
onPostExecute
Runs after doInBackground(). Its parameter is the return value of the doInBackground function, and mainly you put the changes in UI need to be done after the connection is finished (runs on mainUIThread)
You have to declare another class within the class you have already created.
class SomeName extends Async<Void, String, Void>{
protected void OnPreExecute(){
// starts the task runs on main UI thread
// Can be used to start a progress dialog to show the user progress
}
#Override
protected Void doInBackground(Void... params){
// does what you want it to do in the background
// Connected to the server to check a log-in
return result;
}
protected void OnPostExecute(Void result){
// finishes the task and can provide information back on the main UI thread
// this would be were you would dismiss the progress dialog
}
}

Categories

Resources