Search code examples
xmppopenfiresmackasmackaccountmanager

AccountAuthenticatorActivity not getting triggered for adding account for first time in AccountManager


I am developing XMPP based Android Chat client. It works on the Socket connection and to keep connection alive periodic ping to server is required. For this purpose I am using background running android service.

A instance of connection is created in this service. This service exists forever in background and perform job of pinging and receiving messages.

One problem is when chat client is killed from Task Manager, background service RESTARTS (it still keep running because I have declared it START-STICKY).

Once service restarts it clear the connection instance. This cause issue for me since connection instance is cleared its equivalent to connection lost.

So I am recreating connection if its null on onStartCommand of service.

However to make connection I need Username and Password. I want to keep these credentials in AccountManager of Android. (I feel its safer to store password in AccountManager than to use SharedPreferrence or SQLite or FileSystem)

1. Since I am not storing any authentication token, is using AccountManager is right choice in my case? Is it really safer than other storages?

2. Is there any way I can store instance of connection itself the way authentication token is stored in Account Manager?

True to my reading about Account Manager, I have implemented AuthenticationService, AccountAuthenticator and AccountAuthenticator Activity as follows-

AuthenticatationService:

public class AuthenticatationService extends Service {

@Override
public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    return new AccountAuthenticator(this).getIBinder();
}
}

Account Authenticator:

public class AccountAuthenticator extends AbstractAccountAuthenticator {
Context mContext;
public AccountAuthenticator(Context context) {
    super(context);
    mContext=context;
}

@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
    return null;
}

@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {

    Log.d("Mymsg", "AccountAuthenticator > addAccount() called with " + "response = [" + response + "], accountType = [" + accountType + "], authTokenType = [" + authTokenType + "], requiredFeatures = [" + requiredFeatures + "], options = [" + options + "]" + "---->START");

    final Intent intent = new Intent(mContext, LoginActivity.class);

    intent.putExtra(mContext.getString(R.string.intentdatakey_accounttype), accountType);
    intent.putExtra(mContext.getString(R.string.intentdatakey_authtype), authTokenType);
    intent.putExtra(mContext.getString(R.string.intentdatakey_isaddingnewaccount), true);

    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
    final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);

    Log.d("Mymsg", "AccountAuthenticator > addAccount() returned [bundle: " + bundle + "----> STOP");
    return bundle;
}

@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
    return null;
}

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {

    Log.d("Mymsg", "AccountAuthenticator > getAuthToken() called with " + "response = [" + response + "], account = [" + account + "], authTokenType = [" + authTokenType + "], options = [" + options + "]" + "---->START");
    // Extract the username and password from the Account Manager, and ask
    // the server for an appropriate AuthToken.
    final AccountManager am = AccountManager.get(mContext);

    String authToken = am.peekAuthToken(account, authTokenType);

    // Lets give another try to authenticate the user
    if (TextUtils.isEmpty(authToken)) {
        final String password = am.getPassword(account);
        if (password != null) {
            authToken = password;
        }
    }

    // If we get an authToken - we return it
    if (!TextUtils.isEmpty(authToken)) {
        final Bundle result = new Bundle();
        result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
        result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
        result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
        return result;
    }

    // If we get here, then we couldn't access the user's password - so we
    // need to re-prompt them for their credentials. We do that by creating
    // an intent to display our AuthenticatorActivity.
    final Intent intent = new Intent(mContext, LoginActivity.class);
    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
    intent.putExtra(mContext.getString(R.string.intentdatakey_accounttype), account.type);
    intent.putExtra(mContext.getString(R.string.intentdatakey_authtype), authTokenType);
    final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);

    Log.d("Mymsg", "AccountAuthenticator > getAuthToken() returned [bundle: " + bundle + "----> STOP");
    return bundle;
}

@Override
public String getAuthTokenLabel(String authTokenType) {
    return null;
}

@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
    return null;
}

@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
    return null;
}
}

Login Activity:

public class LoginActivity extends AccountAuthenticatorActivity implements View.OnClickListener {

private EditText txtUsername;
private Button btnLogin;
private String username;

private ProgressDialog progressDialog;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);

    txtUsername = (EditText) findViewById(R.id.txtUsername);

    btnLogin = (Button) findViewById(R.id.btnLogin);
    btnLogin.setOnClickListener(this);

    progressDialog = new ProgressDialog(this);

    // Connection and Login Response events
    LocalBroadcastManager.getInstance(this).registerReceiver(connectionSuccessfulReceiver,
            new IntentFilter(getString(R.string.broadcastmessage_connectionsuccessful)));
    LocalBroadcastManager.getInstance(this).registerReceiver(connectionFailureReceiver,
            new IntentFilter(getString(R.string.broadcastmessage_connectionfailure)));
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btnLogin:

            Log.d("Mymsg", "LoginActivity > onClick() called with " + "v = [" + v + "]" + "---->START");

            username = txtUsername.getText().toString();

            if (username.isEmpty()) {
                Toast.makeText(this, "Please enter username", Toast.LENGTH_LONG);
                return;
            }

            progressDialog.setMessage("Logging In...");
            progressDialog.setCancelable(false);
            progressDialog.show();

            final Intent res = new Intent();
            res.putExtra(AccountManager.KEY_ACCOUNT_NAME, username);
            res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, getString(R.string.accounttype));
            res.putExtra(AccountManager.KEY_AUTHTOKEN, username);
            res.putExtra(AccountManager.KEY_PASSWORD, username);

            final Account account = new Account(username, getString(R.string.accounttype));
            if (getIntent().getBooleanExtra(getString(R.string.intentdatakey_isaddingnewaccount), false)) {
                String authtoken = username;
                String authtokenType = getString(R.string.authtype);
                // Creating the account on the device and setting the auth token we got
                // (Not setting the auth token will cause another call to the server to authenticate the user)
                AccountManager.get(this).addAccountExplicitly(account, username, null);
                AccountManager.get(this).setAuthToken(account, authtokenType, authtoken);
            } else {
                AccountManager.get(this).setPassword(account, username);
            }
            setAccountAuthenticatorResult(res.getExtras());
            setResult(RESULT_OK, res);

            Log.d("Mymsg", "LoginActivity > onClick() ----> STOP");

            break;
        default:
            break;
    }
}

/**
 * On Connection Successful
 */
public void onConnectionSuccessful() {
    Log.d("Mymsg", "LoginActivity > onConnectionSuccessful() called with " + "" + "---->START");

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (progressDialog.isShowing()) {
                progressDialog.dismiss();
            }
        }
    });

    finish();
    Log.d("Mymsg", "LoginActivity > onConnectionSuccessful() ----> STOP");
}

/**
 * On Connection Failure
 *
 * @param message- error message
 */
public void onConnectionFailure(final String message) {
    Log.d("Mymsg", "LoginActivity > onConnectionFailure() called with " + "message = [" + message + "]" + "---->START");
    runOnUiThread(new Runnable() {
        @Override
        public void run() {

            if (progressDialog.isShowing()) {
                progressDialog.dismiss();
            }

            Toast.makeText(LoginActivity.this, "Failed connecting: " + message, Toast.LENGTH_LONG).show();
        }
    });

    Log.d("Mymsg", "LoginActivity > onConnectionFailure() ----> STOP");
}


/*
* Recievers for Connection Events
 */
private BroadcastReceiver connectionSuccessfulReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("Mymsg", "LoginActivity > connectionSuccessfulReceiver > onReceive() called with " + "context = [" + context + "], intent = [" + intent + "]" + "---->START");
        onConnectionSuccessful();
        Log.d("Mymsg", "LoginActivity > connectionSuccessfulReceiver > onReceive() returned void ----> STOP");
    }
};

private BroadcastReceiver connectionFailureReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("Mymsg", "LoginActivity > connectionFailureReceiver > onReceive() called with " + "context = [" + context + "], intent = [" + intent + "]" + "---->START");
        onConnectionFailure(intent.getStringExtra(getString(R.string.intentdatakey_failuremessage)));
        Log.d("Mymsg", "LoginActivity > connectionFailureReceiver > onReceive() ----> STOP");
    }
};
}

on resume of every activity I have checked if connection is null, if it is I have started that background service (which I mentioned earlier to maintain connection and ping periodically). This service will establish connection if Account is present in AccountManager and give callbacks to service.

3. Issue I am facing is when Username and Password is not already present in AccountManager. True to my understanding getAuthToken is entry point in Authenticator, if auth token in not present then login activity is called. However if username is not present how to call getAuthToken. Need suggestions.

Following is my background service-

 public class XMPPListener extends Service implements ChatMessageListener, ChatManagerListener, ConnectionListener {

private static XMPPListener instance;
private NotificationCompat.Builder mBuilder;
private NotificationManager mNotificationManager;
private Context mContext;
private AbstractXMPPConnection connection;
private static boolean isConnected = false;
private static boolean isLoggedIn = false;
private User user;

public XMPPListener() {
    Log.d("Mymsg", "XMPPListener > XMPPListener() called with " + "" + "---->START");
    mContext = this;
    instance = this;
    Log.d("Mymsg", "XMPPListener > XMPPListener() > this: " + this);

    //To show notification when app is not foreground
    mBuilder = new NotificationCompat.Builder(mContext);

    Log.d("Mymsg", "XMPPListener > XMPPListener() ----> STOP");
}

/**
 * Get Singleton instance
 *
 * @return instance
 */
public static XMPPListener getInstance(Context context) {

    return instance;
}

@Override
public void processMessage(Chat chat, Message message) {
    Log.d("Mymsg", "XMPPListener > processMessage() called with " + "message = [" + message + "]" + "---->START");

    try {
        // Make entry of message in local database
        makeEntryOfMessageInLocalDatabase(chat, message);

        // Show Notification
        showNotification(chat, message);

        // Broadcast message that new message received
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(new Intent(getString(R.string.localbroadcastaction_messagereceived)));
    } catch (SQLException e) {
        e.printStackTrace();
    }

    Log.d("Mymsg", "XMPPListener > processMessage() returned void ----> STOP");
}

/**
 * Show Notification
 *
 * @param chat
 * @param message
 */
private void showNotification(Chat chat, Message message) {
    mBuilder.setSmallIcon(R.drawable.notification_icon);
    mBuilder.setContentTitle("@" + chat.getParticipant() + " messaged");
    mBuilder.setContentText(message.getBody());

    Intent resultIntent = new Intent(mContext, ChatWindowActivity.class);
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
    stackBuilder.addParentStack(ChatWindowActivity.class);

    // Adds the Intent that starts the Activity to the top of the stack
    stackBuilder.addNextIntent(resultIntent);
    PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    mBuilder.setContentIntent(resultPendingIntent);

    // notificationID allows you to update the notification later on.
    mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(1, mBuilder.build());
}

/**
 * Make entry of message in Local Database
 *
 * @param chat
 * @param message
 * @throws SQLException
 */
private void makeEntryOfMessageInLocalDatabase(Chat chat, Message message) throws SQLException {

    Log.d("Mymsg", "XMPPListener > makeEntryOfMessageInLocalDatabase() called with " + "chat = [" + chat + "], message = [" + message + "]" + "---->START");

    ChatMessage chatMessage = new ChatMessage();

    // This is received message
    chatMessage.setSent(false);

    chatMessage.setThreadId(chat.getThreadID());
    chatMessage.setChatmessage(message.getBody());
    chatMessage.setTimeStamp(new Date());
    chatMessage.setIsRead(false);

    ChatThread chatThread = new ChatThread();
    chatThread.setThreadId(chat.getThreadID());
    chatThread.setUsername(chat.getParticipant());
    chatThread.setLastChatmessage(message.getBody());
    chatThread.setLastMessageTimeStamp(new Date());
    chatThread.setLastMessageSent(false);
    chatThread.setUnreadMessageCount(SampleChatClientAppDataOperations.getInstance(mContext).getUnReadMessageCountInThread(chat.getThreadID()));

    // Make database entry
    SampleChatClientAppData sampleChatClientAppData = new SampleChatClientAppData(mContext);

    // Update messages table
    sampleChatClientAppData.getChatMessageDao().create(chatMessage);

    // Update Chat list table
    sampleChatClientAppData.getChatThreadDao().update(chatThread);

    Log.d("Mymsg", "XMPPListener > makeEntryOfMessageInLocalDatabase() returned void ----> STOP");
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    Log.d("Mymsg", "XMPPListener > onCreate() called with " + "" + "---->START");
    super.onCreate();

    Log.d("Mymsg", "XMPPListener > onCreate() ----> STOP");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d("Mymsg", "XMPPListener > onStartCommand() called with " + "intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]" + "---->START");

    if (connection == null || !connection.isConnected() || !connection.isAuthenticated()) {
        new GetAuthToken().execute();
    }

    int returnValue = super.onStartCommand(intent, flags, startId);
    Log.d("Mymsg", "XMPPListener > onStartCommand() returned [returnValue: " + returnValue + "----> STOP");
    return returnValue;
}

@Override
public void onTaskRemoved(Intent rootIntent) {
    Log.d("Mymsg", "XMPPListener > onTaskRemoved() called with " + "rootIntent = [" + rootIntent + "]" + "---->START");
    super.onTaskRemoved(rootIntent);
    Log.d("Mymsg", "XMPPListener > onTaskRemoved() ----> STOP");
}

@Override
public void onDestroy() {
    Log.d("Mymsg", "XMPPListener > onDestroy() called with " + "" + "---->START");
    super.onDestroy();
    Log.d("Mymsg", "XMPPListener > onDestroy() ----> STOP");
}

/**
 * Getter for connection
 *
 * @return instance of connection
 */
public AbstractXMPPConnection getConnection() {
    return connection;
}

@Override
public void chatCreated(final Chat chat, boolean createdLocally) {

    Log.d("Mymsg", "XMPPListener > chatCreated() called with " + "chat = [" + chat + "], createdLocally = [" + createdLocally + "]" + "---->START");

    if (!createdLocally) {

        // Register listener
        chat.addMessageListener(XMPPListener.getInstance(this));


        checkIfChatThreadExistForUser(chat.getParticipant(), new Handler() {
            @Override
            public void handleMessage(android.os.Message msg) {
                Log.d("Mymsg", "XMPPListener > checkIfChatThreadExistForUser > handleMessage() called with " + "msg = [" + msg + "]" + "---->START");
                super.handleMessage(msg);
                ArrayList<ChatThread> chatThread;
                try {
                    chatThread = (ArrayList<ChatThread>) msg.obj;

                    Log.d("Mymsg", "chatThread: " + chatThread);

                    if (chatThread.isEmpty()) {
                        // If first time chatting with this user create new thread;
                        createChatThreadInLocalDb(chat);
                    }

                } catch (ClassCastException e) {
                    e.printStackTrace();
                }

                Log.d("Mymsg", "XMPPListener > checkIfChatThreadExistForUser > handleMessage() returned null----> STOP");
            }
        }, new Handler() {
            @Override
            public void handleMessage(android.os.Message msg) {
                super.handleMessage(msg);
            }
        });
    }


    Log.d("Mymsg", "XMPPListener > chatCreated() returned void----> STOP");
}

/**
 * Create chat thread in local db
 *
 * @param chat
 */
public void createChatThreadInLocalDb(Chat chat) {

    Log.d("Mymsg", "XMPPListener > createChatThreadInLocalDb() called with " + "chat = [" + chat + "]" + "---->START");

    ChatThread newChatThread = new ChatThread();
    newChatThread.setUsername(chat.getParticipant());
    newChatThread.setThreadId(chat.getThreadID());

    Datasource.addChatThread(this, newChatThread, new Handler() {
        @Override
        public void handleMessage(android.os.Message msg) {

            Log.d("Mymsg", "XMPPListener > addChatThread > createChatThreadInLocalDb > handleMessage() called with " + "msg = [" + msg + "]" + "---->START");
            super.handleMessage(msg);

            Integer newChatThreadCreated = 0;

            try {
                newChatThreadCreated = (Integer) msg.obj;

                if (newChatThreadCreated > 0) {

                    // Broadcast that chat is created
                    LocalBroadcastManager.getInstance(XMPPListener.this).sendBroadcast(new Intent(getString(R.string.localbroadcastaction_chatcreated)));
                }
            } catch (ClassCastException e) {
                e.printStackTrace();
            }

            Log.d("Mymsg", "XMPPListener > addChatThread > handleMessage() ----> STOP");
        }
    }, new Handler() {
        @Override
        public void handleMessage(android.os.Message msg) {
            super.handleMessage(msg);
        }
    });

    Log.d("Mymsg", "XMPPListener > createChatThreadInLocalDb() returned null----> STOP");
}

/**
 * Checks if thread exist for the username else
 * create new one
 *
 * @param fullUsername
 */
public void checkIfChatThreadExistForUser(final String fullUsername, final Handler successHandler, final Handler failureHandler) {
    Datasource.getChatThreadOfUser(this, fullUsername, successHandler, failureHandler);
}

// Internal sync task for connecting to XMPP server
class ConnectXMPP extends AsyncTask<User, Void, Exception> {

    @Override
    protected Exception doInBackground(User... params) {

        Log.d("Mymsg", "ConnectXMPP > doInBackground() called with " + "params = [" + params + "]" + "---->START");

        User user = params[0];

        Log.d("Mymsg", "user: " + user);

        XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
                .setUsernameAndPassword(user.getUsername(), user.getPassword())
                .setServiceName(mContext.getString(R.string.openfire_host))
                .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                .setDebuggerEnabled(true)
                .build();

        SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
        SASLAuthentication.blacklistSASLMechanism("DIGEST-MD5");
        SASLAuthentication.unBlacklistSASLMechanism("PLAIN");

        connection = new XMPPTCPConnection(config);

        try {
            connection.connect();
            isConnected = connection.isConnected();
            Log.d("Mymsg", "isConnected: " + isConnected);

            connection.login();
            isLoggedIn = connection.isAuthenticated();
            Log.d("Mymsg", "isLoggedIn: " + isLoggedIn);

            connection.addConnectionListener(XMPPListener.this);

            // Broadcast message that new message received
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(new Intent(getString(R.string.broadcastmessage_connectionsuccessful)));

        } catch (SmackException e) {
            e.printStackTrace();
            Intent broadcastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
            broadcastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), e.getMessage());
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadcastIntent);
        } catch (IOException e) {
            e.printStackTrace();
            Intent broadcastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
            broadcastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), e.getMessage());
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadcastIntent);
        } catch (XMPPException e) {
            e.printStackTrace();
            Intent broadcastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
            broadcastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), e.getMessage());
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadcastIntent);
        }

        return null;
    }

    @Override
    protected void onPostExecute(Exception e) {
        super.onPostExecute(e);
    }
}

class GetAuthToken extends AsyncTask<Void, Void, Exception> {

    private User user;

    @Override
    protected Exception doInBackground(Void... params) {

        String username = "";
        String password = "";

        AccountManager accountManager = AccountManager.get(XMPPListener.this);
        final Account[] accounts = accountManager.getAccountsByType(getString(R.string.accounttype));

        Account account;
        if (accounts.length <= 0) {
            account = new Account(username, getString(R.string.accounttype));

        } else {
            account = accounts[0];
        }

        try {
            Bundle bundle = accountManager.getAuthToken(account, mContext.getString(R.string.authtype), true, null, null).getResult();
            password = (String) bundle.get(AccountManager.KEY_AUTHTOKEN);
            Log.d("Mymsg", "password: " + password);
            username = password;
        } catch (OperationCanceledException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (AuthenticatorException e) {
            e.printStackTrace();
        }

        if (username.isEmpty() || password.isEmpty()) {
            Intent broadCastIntent = new Intent(getString(R.string.broadcastmessage_connectionfailure));
            broadCastIntent.putExtra(getString(R.string.intentdatakey_failuremessage), getString(R.string.errormessage_usercredentialmissing));
            LocalBroadcastManager.getInstance(mContext).sendBroadcast(broadCastIntent);
            return null;
        }

        user = new User();
        user.setUsername(username);
        user.setPassword(password);

        return null;
    }

    @Override
    protected void onPostExecute(Exception e) {
        super.onPostExecute(e);

        if (user != null) {
            // Connect XMPP
            new ConnectXMPP().execute(user);
        }
    }
}
}

Solution

  • 1. Since I am not storing any authentication token, is using AccountManager is right choice in my case? Is it really safer than other storages?

    It's not safer per se. The account manager just stores username, password and authtokens in "plaintext" in an SQLite database. So if you have root access you usually can easily access these values.

    Having an authenticator is a requirement if you intend to use the sync-adapter mechanism or if you intend to sync to some content providers like the contacts provider or the calendar provider.

    Other than that it provides easy support for multiple accounts and it provides a clean interface to retrieve authentication credentials. The latter nicely decouples the account management/authentication part from the rest of the app.

    To answer the first question: It depends. If you're not familiar with Authenticators and you're only seeking for better security, you're probably better off without using an Authenticator.

    3. Issue I am facing is when Username and Password is not already present in AccountManager. True to my understanding getAuthToken is entry point in Authenticator, if auth token in not present then login activity is called. However if username is not present how to call getAuthToken. Need suggestions.

    That's not how it works. You can only call getAuthToken for an existing account. getAuthToken only invokes an activity if your authenticator returns an Intent. A use case for doing that is when using OAuth2 and the user revoked access of your app. In that case you need to prompt the user to grant access again.

    Note that auth-tokens may be requested by other apps too (given they have the required permissions), so make sure that you don't return the plain text password as the auth-token or make sure that other apps won't receive a token from your authenticator.

    So, another use case for returning an Intent from your authenticator when getAuthToken was called is if you want to prompt the user for permission.

    So here is how it's done:

    Your app should first check if there is an existing account (using getAccountsByType). If there is one you can call getAuthToken, if there are multiple accounts you usually ask the user which one to use if he didn't select any before, then you call getAuthToken for that account, otherwise call addAccount to let the user add an account first.

    If you don't want to grant other apps access to your plain text password, consider to use setPassword and getPassword instead of using an authtoken. The password is private to your app and can not be retrieved by other apps (well, at least that's more difficult). Or consider to not use an Authenticator at all. It's probably not worth the added complexity in your case.