Skip to content

Commit ef419b2

Browse files
committed
DO NOT MERGE
Backport (with modifications ) some changes from Honeycomb, that would allow authenticators to control caching and permissions. This is backward compatible - both new and old authenticators will work with old and new framework, but the functionality will only be present if both sides support it. Change-Id: Ib2838cc2159f45264b38c844cd4c1d6f315d8064
1 parent 919853c commit ef419b2

File tree

4 files changed

+135
-16
lines changed

4 files changed

+135
-16
lines changed

core/java/android/accounts/AccountAuthenticatorCache.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,22 @@
1818

1919
import android.content.pm.PackageManager;
2020
import android.content.pm.RegisteredServicesCache;
21+
import android.content.pm.ResolveInfo;
2122
import android.content.pm.XmlSerializerAndParser;
2223
import android.content.res.Resources;
2324
import android.content.res.TypedArray;
25+
import android.content.ComponentName;
2426
import android.content.Context;
27+
import android.content.Intent;
2528
import android.util.AttributeSet;
29+
import android.util.Log;
2630
import android.text.TextUtils;
2731
import org.xmlpull.v1.XmlPullParser;
2832
import org.xmlpull.v1.XmlSerializer;
2933
import org.xmlpull.v1.XmlPullParserException;
3034

3135
import java.io.IOException;
36+
import java.util.List;
3237

3338
/**
3439
* A cache of services that export the {@link IAccountAuthenticator} interface. This cache
@@ -63,11 +68,40 @@ public AuthenticatorDescription parseServiceAttributes(Resources res,
6368
com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0);
6469
final int prefId = sa.getResourceId(
6570
com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0);
71+
72+
boolean customTokens = false;
73+
try {
74+
// In HC this will be an attribute in authenticator.xml, this is a workaround
75+
// using meta-data to avoid changes to the API.
76+
// If meta-data is absent the old behavior is preserved.
77+
// Authenticator will know if AccountManager supports customTokens or not.
78+
PackageManager pm = mContext.getPackageManager();
79+
List<ResolveInfo> resolveInfos = pm.queryIntentServices(
80+
new Intent(AccountManager.ACTION_AUTHENTICATOR_INTENT),
81+
PackageManager.GET_META_DATA);
82+
for (ResolveInfo resolveInfo: resolveInfos) {
83+
android.content.pm.ServiceInfo si = resolveInfo.serviceInfo;
84+
if (!packageName.equals(si.packageName)) {
85+
continue;
86+
}
87+
Object ctString = si.metaData.get(AccountManager.ACTION_AUTHENTICATOR_INTENT
88+
+ ".customTokens");
89+
if (ctString != null) {
90+
customTokens = true;
91+
}
92+
}
93+
} catch (Throwable t) {
94+
// Protected against invalid data in meta or unexpected
95+
// conditions - the authenticator will not have the new
96+
// features.
97+
Log.e(TAG, "Error getting customTokens metadata " + t);
98+
}
99+
66100
if (TextUtils.isEmpty(accountType)) {
67101
return null;
68102
}
69-
return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
70-
smallIconId, prefId);
103+
return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
104+
smallIconId, prefId, customTokens);
71105
} finally {
72106
sa.recycle();
73107
}

core/java/android/accounts/AccountManager.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,24 @@ public class AccountManager {
188188
public static final String KEY_ERROR_CODE = "errorCode";
189189
public static final String KEY_ERROR_MESSAGE = "errorMessage";
190190
public static final String KEY_USERDATA = "userdata";
191+
/**
192+
* Authenticators using 'customTokens' option will also get the UID of the
193+
* caller
194+
* @hide
195+
*/
196+
public static final String KEY_CALLER_UID = "callerUid";
197+
198+
/**
199+
* @hide
200+
*/
201+
public static final String KEY_CALLER_PID = "callerPid";
202+
203+
/**
204+
* Boolean, if set and 'customTokens' the authenticator is responsible for
205+
* notifications.
206+
* @hide
207+
*/
208+
public static final String KEY_NOTIFY_ON_FAILURE = "notifyOnAuthFailure";
191209

192210
public static final String ACTION_AUTHENTICATOR_INTENT =
193211
"android.accounts.AccountAuthenticator";

core/java/android/accounts/AccountManagerService.java

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public class AccountManagerService
9191

9292
private final Context mContext;
9393

94+
private final PackageManager mPackageManager;
95+
9496
private HandlerThread mMessageThread;
9597
private final MessageHandler mMessageHandler;
9698

@@ -214,6 +216,7 @@ public int hashCode() {
214216

215217
public AccountManagerService(Context context) {
216218
mContext = context;
219+
mPackageManager = context.getPackageManager();
217220

218221
mOpenHelper = new DatabaseHelper(mContext);
219222

@@ -520,6 +523,18 @@ public void removeAccount(IAccountManagerResponse response, Account account) {
520523
if (account == null) throw new IllegalArgumentException("account is null");
521524
checkManageAccountsPermission();
522525
long identityToken = clearCallingIdentity();
526+
527+
cancelNotification(getSigninRequiredNotificationId(account));
528+
synchronized(mCredentialsPermissionNotificationIds) {
529+
for (Pair<Pair<Account, String>, Integer> pair:
530+
mCredentialsPermissionNotificationIds.keySet()) {
531+
if (account.equals(pair.first.first)) {
532+
int id = mCredentialsPermissionNotificationIds.get(pair);
533+
cancelNotification(id);
534+
}
535+
}
536+
}
537+
523538
try {
524539
new RemoveAccountSession(response, account).bind();
525540
} finally {
@@ -842,19 +857,49 @@ public void onResult(Bundle result) {
842857

843858
public void getAuthToken(IAccountManagerResponse response, final Account account,
844859
final String authTokenType, final boolean notifyOnAuthFailure,
845-
final boolean expectActivityLaunch, final Bundle loginOptions) {
860+
final boolean expectActivityLaunch, Bundle loginOptionsIn) {
861+
if (Log.isLoggable(TAG, Log.VERBOSE)) {
862+
Log.v(TAG, "getAuthToken: " + account
863+
+ ", response " + response
864+
+ ", authTokenType " + authTokenType
865+
+ ", notifyOnAuthFailure " + notifyOnAuthFailure
866+
+ ", expectActivityLaunch " + expectActivityLaunch
867+
+ ", caller's uid " + Binder.getCallingUid()
868+
+ ", pid " + Binder.getCallingPid());
869+
}
846870
if (response == null) throw new IllegalArgumentException("response is null");
847871
if (account == null) throw new IllegalArgumentException("account is null");
848872
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
849873
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
850874
final int callerUid = Binder.getCallingUid();
851-
final boolean permissionGranted = permissionIsGranted(account, authTokenType, callerUid);
875+
final int callerPid = Binder.getCallingPid();
876+
877+
AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
878+
mAuthenticatorCache.getServiceInfo(
879+
AuthenticatorDescription.newKey(account.type));
880+
final boolean customTokens =
881+
authenticatorInfo != null && authenticatorInfo.type.customTokens;
882+
883+
// skip the check if customTokens
884+
final boolean permissionGranted = customTokens ||
885+
permissionIsGranted(account, authTokenType, callerUid);
886+
887+
final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
888+
loginOptionsIn;
889+
if (customTokens) {
890+
// let authenticator know the identity of the caller
891+
loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
892+
loginOptions.putInt(AccountManager.KEY_CALLER_PID, callerPid);
893+
if (notifyOnAuthFailure) {
894+
loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
895+
}
896+
}
852897

853898
long identityToken = clearCallingIdentity();
854899
try {
855900
// if the caller has permission, do the peek. otherwise go the more expensive
856901
// route of starting a Session
857-
if (permissionGranted) {
902+
if (!customTokens && permissionGranted) {
858903
String authToken = readAuthTokenFromDatabase(account, authTokenType);
859904
if (authToken != null) {
860905
Bundle result = new Bundle();
@@ -908,12 +953,14 @@ public void onResult(Bundle result) {
908953
"the type and name should not be empty");
909954
return;
910955
}
911-
saveAuthTokenToDatabase(new Account(name, type),
912-
authTokenType, authToken);
956+
if (!customTokens) {
957+
saveAuthTokenToDatabase(new Account(name, type),
958+
authTokenType, authToken);
959+
}
913960
}
914961

915962
Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
916-
if (intent != null && notifyOnAuthFailure) {
963+
if (intent != null && notifyOnAuthFailure && !customTokens) {
917964
doNotification(
918965
account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
919966
intent);
@@ -972,6 +1019,10 @@ private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
9721019
AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
9731020

9741021
Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
1022+
// See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
1023+
// Since it was set in Eclair+ we can't change it without breaking apps using
1024+
// the intent from a non-Activity context.
1025+
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
9751026
intent.addCategory(
9761027
String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
9771028

@@ -1849,12 +1900,12 @@ private void checkBinderPermission(String... permissions) {
18491900
}
18501901

18511902
private boolean inSystemImage(int callerUid) {
1852-
String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
1903+
String[] packages = mPackageManager.getPackagesForUid(callerUid);
18531904
for (String name : packages) {
18541905
try {
1855-
PackageInfo packageInfo =
1856-
mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
1857-
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
1906+
PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
1907+
if (packageInfo != null
1908+
&& (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
18581909
return true;
18591910
}
18601911
} catch (PackageManager.NameNotFoundException e) {
@@ -1872,7 +1923,7 @@ private boolean permissionIsGranted(Account account, String authTokenType, int c
18721923
&& hasExplicitlyGrantedPermission(account, authTokenType);
18731924
if (Log.isLoggable(TAG, Log.VERBOSE)) {
18741925
Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
1875-
+ callerUid + ", account " + account
1926+
+ callerUid + ", " + account
18761927
+ ": is authenticator? " + fromAuthenticator
18771928
+ ", has explicit permission? " + hasExplicitGrants);
18781929
}
@@ -1884,7 +1935,7 @@ private boolean hasAuthenticatorUid(String accountType, int callingUid) {
18841935
mAuthenticatorCache.getAllServices()) {
18851936
if (serviceInfo.type.type.equals(accountType)) {
18861937
return (serviceInfo.uid == callingUid) ||
1887-
(mContext.getPackageManager().checkSignatures(serviceInfo.uid, callingUid)
1938+
(mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
18881939
== PackageManager.SIGNATURE_MATCH);
18891940
}
18901941
}

core/java/android/accounts/AuthenticatorDescription.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,16 @@ public class AuthenticatorDescription implements Parcelable {
4444
/** The package name that can be used to lookup the resources from above. */
4545
final public String packageName;
4646

47-
/** A constructor for a full AuthenticatorDescription */
47+
/** Authenticator handles its own token caching and permission screen
48+
* @hide
49+
*/
50+
final public boolean customTokens;
51+
52+
/** A constructor for a full AuthenticatorDescription
53+
* @hide
54+
*/
4855
public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
49-
int smallIconId, int prefId) {
56+
int smallIconId, int prefId, boolean customTokens) {
5057
if (type == null) throw new IllegalArgumentException("type cannot be null");
5158
if (packageName == null) throw new IllegalArgumentException("packageName cannot be null");
5259
this.type = type;
@@ -55,6 +62,12 @@ public AuthenticatorDescription(String type, String packageName, int labelId, in
5562
this.iconId = iconId;
5663
this.smallIconId = smallIconId;
5764
this.accountPreferencesId = prefId;
65+
this.customTokens = customTokens;
66+
}
67+
68+
public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
69+
int smallIconId, int prefId) {
70+
this(type, packageName, labelId, iconId, smallIconId, prefId, false);
5871
}
5972

6073
/**
@@ -74,6 +87,7 @@ private AuthenticatorDescription(String type) {
7487
this.iconId = 0;
7588
this.smallIconId = 0;
7689
this.accountPreferencesId = 0;
90+
this.customTokens = false;
7791
}
7892

7993
private AuthenticatorDescription(Parcel source) {
@@ -83,6 +97,7 @@ private AuthenticatorDescription(Parcel source) {
8397
this.iconId = source.readInt();
8498
this.smallIconId = source.readInt();
8599
this.accountPreferencesId = source.readInt();
100+
this.customTokens = source.readByte() == 1;
86101
}
87102

88103
/** @inheritDoc */
@@ -115,6 +130,7 @@ public void writeToParcel(Parcel dest, int flags) {
115130
dest.writeInt(iconId);
116131
dest.writeInt(smallIconId);
117132
dest.writeInt(accountPreferencesId);
133+
dest.writeByte((byte) (customTokens ? 1 : 0));
118134
}
119135

120136
/** Used to create the object from a parcel. */

0 commit comments

Comments
 (0)