Wednesday, 16 May 2012

Android - Account manager - Part I

Note: Here is the second parte of this post
http://www.jiahaoliuliu.com/2012/06/android-account-manager-part-ii.html

Update: The sample code has been uploaded to GitHub. Any comments are welcome.
https://github.com/jiahaoliuliu/sampleaccountandserver

1. Introduction
By default, Android incorporates an Account manager, which is used to store user's credentials and sync with the server, if necessary. It's use has been encouraged by the Google's guy on Android I/O 2011:







2. Basic notions
The account manager is a centralized service offered by Android system. Any application can get the list of accounts and request the user to utilize its auth tokens.

Basically it contains a list of Accounts, each one is identified by:
  • Account name: The user name used to log in. For example, jiahaoliuliu
  • Account type: The type of the account. For example, com.google
All the accounts should be unique across a device. This is, for a given device, there cannot be any two accounts that have the same account name and account type.

For each one of the accounts, there is a set of data related with it:
  • Password: The password of the account. It could be empty("").
  • AuthTokens: The String used by the server to identify the user, instead of the password. Normally the auth token is temporary and it will be expired after a time. All the auth tokens have a type, called AuthTokenType. This is because one account could be used for several services, and for each one of the service there could be different auth tokens. For example, the account of Google could be used for Gmail and Youtube. The authTokenType of Gmail is "mail" and the authTokenType of YouTube is youtube. Check this web page for more information.
  • UserDatas: Additionally the user can save user data as the pair key/value of type String. This is useful when extra data are associated with the account whom are not password neither auth token.
3. Permissions
There are several permission required to interact with the account manager:
  • android.permission.GET_ACCOUNTS
This permission is utilized to get the list of accounts and check if a specific account has a specific feature. (The features will not be talked in this post.)
  • android.permission.USE_CREDENTIALS
This permission is utilized to get and invalidate auth tokens.
  • android.permission.AUTHENTICATE_ACCOUNTS
This permission is utilized to modify the information related with the account, such as the password, the user data, the auth tokens, etc. It is also used to creates accounts and get the password.
  • android.permission.MANAGE_ACCOUNTS
This permission is utilized to add or remove accounts. It could be needed when the application utilizes any advanced features of the app, such as update credentials or edit properties.


4. Managing data with the account manager
4.1 Account manager
The account manager is a centralized service. The applications cannot creates a new account manager but get the existence one passing the context. This is

AccountManager accountManager = AccountManager.get(Context)

Where Context is the context of the application.

4.2 Accounts
4.2.1 Add a new account
The easiest way to add a new account is utilize the method:
  • boolean addAccountExplicitly(Account account, String password, Bundle userdata)
Notices the class Account could be easily built with:

Account account = new Account(userName, accountType)

Both of the parameters are String.

4.2.2 Get an account
The application can get information about all the accounts of the system. There are two main methods:
  • Account[] getAccount()
  • Account[] getAccountsByType(String type)
The first method get all the accounts in the account manager and the second one, get only the accounts that matches with a specific type.

All the accounts have two fields:
  • name: accessible using account.name
  • type: accessible using account.type
Thus, an easy way to get an account of type "com.jiahaoliuliu" which has the user name "jiahaoliuliu" is:

        Account[] accounts = accountManager.getAccountsByType("com.jiahaoliuliu");
        Account myAccount = null;
        for (Account account : accounts) {
            if (account.name.equalsIgnoreCase("jiahaoliuliu")) {
                myAccount = account;
                break;
            }
        }

4.2.3 Remove an account
The accounts could be removed by:
  • AccountManagerFuture<Boolean> removeAccount(Account account, AccountManagerCallback<Boolean> callback, Handler handler)
Notice that for each account, there could be some settings that prevent the account to be deleted. So, it does not guarantee the success.

4.3 Password
The password is a very sensible data that shouldn't be stored as it. One of the big security problem is it saves the password as plain text, without any kind of encryption. Check the security section of the second part of this post for more information.

4.3.1 Set the password
For a given account, the password could be set in two ways:
  • When the account is created by the first time, using the method

boolean addAccountExplicitly (Account account, String password, Bundle userdata)

  • After the account has been created

void setPassword(Account account, String password)

4.3.2 Get the password
The password could be retrieved by the method:

String getPassword(Account)

4.3.3 Clear the password
There is a specific method used to clear the password.

void clearPassword(Account)

The method above has the same effect than:

setPassword(Account, null)

4.4 Auth tokens
As I have explained before, one account could a set of auth tokens, each one for a specific service, identified by the authTokenType.

4.4.1 Set the auth token
To set an auth token, the follow method could be used:
  • void setAuthToken(Account account, String authTokenType, String authType)
4.4.2 Get the auth token
There are several way to get the auth tokens. The easiest way is utilize the follow one:
  • AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType, boolean notifyAuthFailure, AccountManagerCallback<Bundle> callback, Handler handler)
Here is a good example of usage:

4.4.3 Invalidate the auth token
Because normally the auth tokens have an expiration time, it is a good practice to invalidate an auth token when it is not valid any more. To do it so, there is a specific method:
  • void invalidateAuthToken(String accountType, String authToken)
Notice that the use must enter the complete authToken to invalidate it.

4.5 User data
Additionally the app can enter any extra information related with the account by using the user data. It is only a map of key/value which all them are String.

4.5.1 Set the user data
As the password, the user data can be both set when the account is being created or later, when the account already exists.
  • boolean addAccountExplicitly(Account account, String password, Bundle userData)
  • void setUserData(Account account, String key, String value)
4.5.2 Get the user data
There is only one method to get the user data.
  • String getUserData(Account account, String key)
4.6 On account updated listener
The account manager offers the possibility to add an account update listener to act when an account has updated. The listener will be apply to the actual instance of the AccountManager.
  • void addOnAccountsUpdatedListener(OnAccountsUpdateListener listener, Handler handler, boolean updateImmediately)
  • void removeOnAccountsUpdatedListener(OnAccountsUpdatedListener listener)
It is important to notice that while an instance of Account manager is being listened, it won't be collected by the account manager, neither the context used to retrieve it. To avoid memory leaks, it is important remove the listener if it is not going to be used. For example, onDestroy().

5. Partial conclusion
The account manager is very useful to have all the accounts centralized in one place, But it has some inconvenience. Check the second part of this tutorial for more information.

6 comments:

  1. Nice article.

    Salutations from Vigo.

    ReplyDelete
  2. What about the security of the password? I see nothing about this in the second part. Would be nice to see an update.

    Otherwise a good post. Thanx!

    ReplyDelete
    Replies
    1. You must implements the security part by yourself. You might try to find some AES example in the internet, which accepts a customized Seed.

      Delete
  3. Simple, extensive and to the point. Excellent article!

    ReplyDelete