SecurityException: caller uid XXXX is different than the authenticator's uid

Solution 1:

Some other useful tips to debug problems like this.

First enable verbose logging for some tags:

$ adb shell setprop log.tag.AccountManagerService VERBOSE
$ adb shell setprop log.tag.Accounts VERBOSE
$ adb shell setprop log.tag.Account VERBOSE
$ adb shell setprop log.tag.PackageManager VERBOSE

You'll see logging like this:

V/AccountManagerService: initiating bind to authenticator type com.example.account
V/Accounts: there is no service connection for com.example.account
V/Accounts: there is no authenticator for com.example.account, bailing out
D/AccountManagerService: bind attempt failed for Session: expectLaunch true, connected false, stats (0/0/0), lifetime 0.002, addAccount, accountType com.example.account, requiredFeatures null

Which means that there is no authenticator registered for this account type. To see which authenticators are registered watch the log when installing the package:

D/PackageManager: encountered new type: ServiceInfo: AuthenticatorDescription {type=com.example.account}, ComponentInfo{com.example/com.example.android.AuthenticatorService}, uid 10028
D/PackageManager: notifyListener: AuthenticatorDescription {type=com.example.account} is added

I had the problem that the authenticator xml descriptor referred to a string resource which didn't get resolved properly during the installation:

android:accountType="@string/account_type"

The logs showed

encountered new type: ServiceInfo: AuthenticatorDescription {type=@2131231194}, ...

Replacing it with a normal string (not resource) solved the problem. This seems to be Android 2.1 specific.

android:accountType="com.example.account"

Solution 2:

First, check the condition explained on this post:

[...] If you see an error from the AccountManagerService of the form caller uid XXXX is different than the authenticator's uid, it might be a bit misleading. The ‘authenticator’ in that message is not your authenticator class, it’s what Android understands to be the registered authenticator for the account’s type. The check that happens within the AccountManagerService looks like this:

 private void checkCallingUidAgainstAuthenticator(Account account) {
     final int uid = Binder.getCallingUid();
     if (account == null || !hasAuthenticatorUid(account.type, uid)) {
         String msg = "caller uid " + uid + " is different than the authenticator's uid";
         Log.w(TAG, msg);
         throw new SecurityException(msg);
     }
     if (Log.isLoggable(TAG, Log.VERBOSE)) {
         Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid");
     }
 }

Note that hasAuthenticatorUid() takes the account.type. This is where I’d screwed up. I was creating my Account with a type specified by a constant:

 class LoginTask {
     Account account = new Account(userId, AuthenticatorService.ACCOUNT_TYPE);
     ...
 }

 class AuthenticatorService extends Service {
     public static final String ACCOUNT_TYPE = "com.joelapenna.foursquared";
     ...
 }

but this constant did not match the XML definition for my authenticator:

 <account-authenticator xmlns:android="/web/20150729061818/http://schemas.android.com/apk/res/android"
        android:accountType="com.joelapenna.foursquared.account" ... />

Second, if you are like me and want to embed the sample into your existing app for testing then, make sure you use Constants class that is part of this example and not under android.provider.SyncStateContract package. Because both classes use the same attribute name ACCOUNT_TYPE that is used when creating Account object.

Solution 3:

In my case the problem was very simply a mismatch in accountType declared in res/xml/authenticator.xml as android:accountType="com.foo" but referenced incorrectly as "foo.com" in creating the Account:

Account newAccount = new Account("dummyaccount", "foo.com");

Doh!

Solution 4:

There are few parts to implement custom account...

To invoke AccountManager in your Activity, something like that you already implemented...

Account account = new Account(username, ACCESS_TYPE);
AccountManager am = AccountManager.get(this);
Bundle userdata = new Bundle();
userdata.putString("SERVER", "extra");

if (am.addAccountExplicitly(account, password, userdata)) {
    Bundle result = new Bundle();
    result.putString(AccountManager.KEY_ACCOUNT_NAME, username);
    result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCESS_TYPE);
    setAccountAuthenticatorResult(result);
}

In res/xml/authenticator.xml you have to define your AccountAuthenticator data (responsible for your Authenticator UID). ACCESS_TYPE have to be the same string as your defined accountType in this xml!

<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="de.buecherkiste"
    android:icon="@drawable/buecher"
    android:label="@string/app_name"
    android:smallIcon="@drawable/buecher" >
</account-authenticator>

Finally you have to define your service your Manifest. Please do not forget the relevant permissions for manage your accounts (AUTHENTICATE_ACCOUNTS / USE_CREDENTIALS / GET_ACCOUNTS / MANAGE_ACCOUNTS)

<service android:name=".AuthenticationService">
    <intent-filter>
        <action android:name="android.accounts.AccountAuthenticator" />
    </intent-filter>
    <meta-data android:name="android.accounts.AccountAuthenticator"
        android:resource="@xml/authenticator" />
</service>