Android Single Account, Multiple Application Prescription

23 09 2011

One of the really neat things that Google has done with all of its Android applications is that they all share a common authentication mechanism in Accounts and Sync. There is no need to enter your Google credentials for each Google based application on your device, which centralizes password changes to a single location. It also groups those common authentication applications together in the Accounts and Sync settings activity, as I’m sure everyone is familiar with:

device-2011-09-16-084016-2011-09-23-09-03.jpg

For my employer, we have several internal Android applications under development and we too wanted to employ this single mechanism to support multiple accounts paradigm in use by Google. How to do it is in the documentation, but I had to hunt for it a bit. How you obtain the token in the authentication mechanism and how you use that token in your service call is out of scope for this prescription. However, this prescription will show you how to tie all these pieces together:

1. Create your authenticator (out of scope for this).  Consult a previous blog I have on this subject for that prescription.

2. In the manifest for the application containing the authentication mechanism, alter the sharedUserId and put a static value.  Here, we’ll use com.captechventures.mobile.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.captechventures.android.authenticator"
      android:versionCode="1"
      android:versionName="1.0.0"
      android:sharedUserId="com.captechventures.mobile">

3. In the authenticator descriptor (authenticator.xml in my example), add an label to give the grouping of accounts a generic name. While not necessary, this will allow you to name the account something other than the name of the application it’s installed with (default behavior).

<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="com.captechventures.mobile"
    android:icon="@drawable/icon"
    android:smallIcon="@drawable/icon"
    android:label="@string/auth_token_label"
/>

4. Create the 2nd (or 3rd, 4th, etc) application that needs to leverage that authentication mechanism with a sync adapter. Follow the prescription outlined in step 1, however skip the steps to create the authentication mechanism. You’re still going to need a SyncAdapter.

5. In the sync adapter descriptor (syncadapter.xml in my example), specify the same account as defined by the authentication mechanism created in step #1 in the accountType attribute.

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:contentAuthority="com.captechventures.test.hc1"
    android:accountType="com.captechventures.mobile"
    android:supportsUploading="false"
/>

6. In the 2nd application’s manifest, specify a sharedUserId value and use the same value that was used in step #2. Optionally, provide a sharedUserLabel as this will be the name of your application under the account grouping.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.captechventures.test.hc1" android:versionCode="1"
android:versionName="1.0" android:sharedUserId="com.captechventures.mobile" android:sharedUserLabel="@string/app_name">
<uses-sdk android:minSdkVersion="8" />

7. When ready to publish your application, you must sign your 2nd application with the same certificate used to sign your first app. Without signing your application, Android will not allow your two apps to share resources (which you’ve specified you’d like to do with the same sharedUserId attribute in the manifest).

You now have 2 applications that can share/use the same authentication mechanism to your back end services! This makes credential management much easier on the user of your apps. A couple of points to remember:

1. When you request a sync with a content resolver, you MUST provide the Account with the appropriate accountType created in step 1 in the API call.

2. Clients are responsible for identifying if the authentication token is invalid, by making an API call to the AccountManager.

mAccountManager.invalidateAuthToken("com.captechventures.mobile", authToken);

3. The 2nd application requires that the 1st application be installed on the device for it work. You should install the authenticator with an application that will always be on your users’ devices or think of breaking that mechanism out into its own application.

Advertisements




Connecting The Dots with Android SyncAdapter

22 09 2010

I recently had an opportunity to work with the Android SDK. This was my first foray into Android development and I found it interesting and rewarding. One of my tasks was to look into creating a sync adapter, responsible for syncing our local device storage with a RESTful service in the cloud. While a sync example is provided in the SDK and the Android javadocs are fruitful, there was still a certain lack of prescription that I find necessary when looking into a new technology. This blog will provide the necessary steps in a prescriptive format, hoping to help any others struggling with this topic.

Before getting started, I absolutely must recommend viewing the 2010 Google I/O presentation on Android REST Client Applications, presented by Virgil Dobjanschi. It’s a fantastic introduction into some of the concepts and recommended patterns for writing well design syncing applications. Along with this presentation and the sync adapter sample provided with the SDK, the following list should provide you ample ammunition to get a sync adapter up and running fairly quickly.

1. ContentProvider
There are two important Android components that are required for your sync adapter, the basic being a ContentProvider. The content provider should contain data you want to sync. Content providers are generally backed by one of the storage mechanisms in Android: Shared Preferences, Internal Storage, External Storage, SQLite Database or network storage. Writing a content provider is outside the scope of this entry. However, the main thing to note when implementing a content provider is the authority you assign in the Android manifest. It will be used in another step to tie some of our components together. In this example, it’s com.captechventures.unanet seen in this snippet:

<provider android:name=".content.UnanetProvider"
        android:authorities="com.captechventures.unanet" android:enabled="true"></provider>

2. Account
The other important component is that you must have an account registered with the Android OS that you intend to link to your sync adapter. The bad news is that there is no “stock” functionality to give you an easy way to provide an Account to the system. However, in the same Sync Adapter Example that comes with the SDK there is a lot of code you can borrow to give you Account functionality. Unless you desire a custom credentials screen, you can heist all the code in the com.example.android.samplesync.authenticator package with only a few minor changes. You’ll also need to snag the authenticator.xml from the resource directory. There is a reference to Constants.ACCOUNT_TYPE in the Authenticator class. Be sure to replace this value with your own account type value, such as com.captechventures.unanet.account which is what we used for our example. You’ll also need to change the accountType attribute in the authenticator.xml to match the value you just used for replacement.

3. Sync Adapter Descriptor
This is the file that ties your sync adapter to the content provider and the account. In the res/xml folder, you’ll need to place an xml file that describes your sync adapter. It doesn’t matter the name, the contents are what’s important (that was foreign to me). Here is my example, found in the res/xml folder and named syncadapter.xml:

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:contentAuthority="com.captechventures.unanet"
    android:accountType="com.captechventures.unanet.account"
    android:supportsUploading="false"
/>

Recognize the contentAuthority and accountType attributes and their values. This is how we tie our content provider and our account to our sync adapter.

4. Abstract Threaded Sync Adapter Implementation
You’ll need to implement an AbstractThreadedSyncAdapter. Here is where the meat of your sync logic will live/start. There’s no one right way for sync logic and it will be different from implementation to implementation. However, I highly recommend following one of the patterns Dobjanschi outlines in his presentation for managing the sync itself. Once you watch the presentation and absorb the information, it just “makes sense” and doesn’t seem like there could be a better way.

Be sure to use the SyncResult in your overridden onPerformSync method appropriately. Using the object in the appropriate manner will allow the sync manager to be more effective.

5. Sync Adapter Service
The syncing mechanism will run in the background on a different thread from the UI. To do this, we’ll need to setup a service that filters on sync intents. The service implementation is simple and only a few lines long. Follow the example in the Sync Adapter Sample, changing the types to match yours. Here is the declaration in the Android Manifest for my sample service, notice the addition of the intent filter and the android:resource reference to the Sync Adapter Descriptor we created in step 3:

<service android:name=".sync.SyncService"
        android:exported="true">
        <intent-filter>
                <action android:name="android.content.SyncAdapter" />
        </intent-filter>
        <meta-data android:name="android.content.SyncAdapter"
                android:resource="@xml/syncadapter" />
</service>

6. Android Manifest
The final piece is to pull everything together in your Android Manifest. We’ve already identified you’ll need your content provider as well as the Service responsible for reacting to sync manager requests declared. You’ll also need to provide a few permissions to allow several aspects of your application to function appropriately: ability to read/write accounts, ability to interact with the network, and ability to read/write sync settings. Here is a snippet from our sync adapter:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />

Conclusion
While this roadmap is aimed at kickstarting your sync adapter development, it by no means replaces reading and understanding the Android documentation on sync adapters and all the related classes and components. Be sure to read the documentation linked throughout this entry.