summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Costin Manolache <costin@google.com> 2011-03-01 12:25:21 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2011-03-01 12:25:21 -0800
commitdb52ab69f22e24615eaa2e8f9845e157426d3dd6 (patch)
tree5a6bd8a3ff0e651406b05a8e610d4501cc6b004e
parent61c1bb4fef439650a3b88131c6e1360ffc03a7ed (diff)
parentef419b2e14e292bf4200a0281ce9125eda431c12 (diff)
Merge "DO NOT MERGE Backport (with modifications ) some changes from Honeycomb, that would allow authenticators to control caching and permissions." into gingerbread
-rw-r--r--core/java/android/accounts/AccountAuthenticatorCache.java38
-rw-r--r--core/java/android/accounts/AccountManager.java18
-rw-r--r--core/java/android/accounts/AccountManagerService.java75
-rw-r--r--core/java/android/accounts/AuthenticatorDescription.java20
4 files changed, 135 insertions, 16 deletions
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index d2b3bc77e9c5..5ee1f60cd05c 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -18,17 +18,22 @@ package android.accounts;
import android.content.pm.PackageManager;
import android.content.pm.RegisteredServicesCache;
+import android.content.pm.ResolveInfo;
import android.content.pm.XmlSerializerAndParser;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.util.AttributeSet;
+import android.util.Log;
import android.text.TextUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.List;
/**
* A cache of services that export the {@link IAccountAuthenticator} interface. This cache
@@ -63,11 +68,40 @@ import java.io.IOException;
com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0);
final int prefId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0);
+
+ boolean customTokens = false;
+ try {
+ // In HC this will be an attribute in authenticator.xml, this is a workaround
+ // using meta-data to avoid changes to the API.
+ // If meta-data is absent the old behavior is preserved.
+ // Authenticator will know if AccountManager supports customTokens or not.
+ PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> resolveInfos = pm.queryIntentServices(
+ new Intent(AccountManager.ACTION_AUTHENTICATOR_INTENT),
+ PackageManager.GET_META_DATA);
+ for (ResolveInfo resolveInfo: resolveInfos) {
+ android.content.pm.ServiceInfo si = resolveInfo.serviceInfo;
+ if (!packageName.equals(si.packageName)) {
+ continue;
+ }
+ Object ctString = si.metaData.get(AccountManager.ACTION_AUTHENTICATOR_INTENT
+ + ".customTokens");
+ if (ctString != null) {
+ customTokens = true;
+ }
+ }
+ } catch (Throwable t) {
+ // Protected against invalid data in meta or unexpected
+ // conditions - the authenticator will not have the new
+ // features.
+ Log.e(TAG, "Error getting customTokens metadata " + t);
+ }
+
if (TextUtils.isEmpty(accountType)) {
return null;
}
- return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
- smallIconId, prefId);
+ return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
+ smallIconId, prefId, customTokens);
} finally {
sa.recycle();
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index fd3a0d046486..677b9597115c 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -188,6 +188,24 @@ public class AccountManager {
public static final String KEY_ERROR_CODE = "errorCode";
public static final String KEY_ERROR_MESSAGE = "errorMessage";
public static final String KEY_USERDATA = "userdata";
+ /**
+ * Authenticators using 'customTokens' option will also get the UID of the
+ * caller
+ * @hide
+ */
+ public static final String KEY_CALLER_UID = "callerUid";
+
+ /**
+ * @hide
+ */
+ public static final String KEY_CALLER_PID = "callerPid";
+
+ /**
+ * Boolean, if set and 'customTokens' the authenticator is responsible for
+ * notifications.
+ * @hide
+ */
+ public static final String KEY_NOTIFY_ON_FAILURE = "notifyOnAuthFailure";
public static final String ACTION_AUTHENTICATOR_INTENT =
"android.accounts.AccountAuthenticator";
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 9a8cc15a60e4..46e25745b859 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -91,6 +91,8 @@ public class AccountManagerService
private final Context mContext;
+ private final PackageManager mPackageManager;
+
private HandlerThread mMessageThread;
private final MessageHandler mMessageHandler;
@@ -214,6 +216,7 @@ public class AccountManagerService
public AccountManagerService(Context context) {
mContext = context;
+ mPackageManager = context.getPackageManager();
mOpenHelper = new DatabaseHelper(mContext);
@@ -520,6 +523,18 @@ public class AccountManagerService
if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission();
long identityToken = clearCallingIdentity();
+
+ cancelNotification(getSigninRequiredNotificationId(account));
+ synchronized(mCredentialsPermissionNotificationIds) {
+ for (Pair<Pair<Account, String>, Integer> pair:
+ mCredentialsPermissionNotificationIds.keySet()) {
+ if (account.equals(pair.first.first)) {
+ int id = mCredentialsPermissionNotificationIds.get(pair);
+ cancelNotification(id);
+ }
+ }
+ }
+
try {
new RemoveAccountSession(response, account).bind();
} finally {
@@ -842,19 +857,49 @@ public class AccountManagerService
public void getAuthToken(IAccountManagerResponse response, final Account account,
final String authTokenType, final boolean notifyOnAuthFailure,
- final boolean expectActivityLaunch, final Bundle loginOptions) {
+ final boolean expectActivityLaunch, Bundle loginOptionsIn) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getAuthToken: " + account
+ + ", response " + response
+ + ", authTokenType " + authTokenType
+ + ", notifyOnAuthFailure " + notifyOnAuthFailure
+ + ", expectActivityLaunch " + expectActivityLaunch
+ + ", caller's uid " + Binder.getCallingUid()
+ + ", pid " + Binder.getCallingPid());
+ }
if (response == null) throw new IllegalArgumentException("response is null");
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
final int callerUid = Binder.getCallingUid();
- final boolean permissionGranted = permissionIsGranted(account, authTokenType, callerUid);
+ final int callerPid = Binder.getCallingPid();
+
+ AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
+ mAuthenticatorCache.getServiceInfo(
+ AuthenticatorDescription.newKey(account.type));
+ final boolean customTokens =
+ authenticatorInfo != null && authenticatorInfo.type.customTokens;
+
+ // skip the check if customTokens
+ final boolean permissionGranted = customTokens ||
+ permissionIsGranted(account, authTokenType, callerUid);
+
+ final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
+ loginOptionsIn;
+ if (customTokens) {
+ // let authenticator know the identity of the caller
+ loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
+ loginOptions.putInt(AccountManager.KEY_CALLER_PID, callerPid);
+ if (notifyOnAuthFailure) {
+ loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
+ }
+ }
long identityToken = clearCallingIdentity();
try {
// if the caller has permission, do the peek. otherwise go the more expensive
// route of starting a Session
- if (permissionGranted) {
+ if (!customTokens && permissionGranted) {
String authToken = readAuthTokenFromDatabase(account, authTokenType);
if (authToken != null) {
Bundle result = new Bundle();
@@ -908,12 +953,14 @@ public class AccountManagerService
"the type and name should not be empty");
return;
}
- saveAuthTokenToDatabase(new Account(name, type),
- authTokenType, authToken);
+ if (!customTokens) {
+ saveAuthTokenToDatabase(new Account(name, type),
+ authTokenType, authToken);
+ }
}
Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
- if (intent != null && notifyOnAuthFailure) {
+ if (intent != null && notifyOnAuthFailure && !customTokens) {
doNotification(
account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
intent);
@@ -972,6 +1019,10 @@ public class AccountManagerService
AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
+ // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
+ // Since it was set in Eclair+ we can't change it without breaking apps using
+ // the intent from a non-Activity context.
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addCategory(
String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
@@ -1849,12 +1900,12 @@ public class AccountManagerService
}
private boolean inSystemImage(int callerUid) {
- String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
+ String[] packages = mPackageManager.getPackagesForUid(callerUid);
for (String name : packages) {
try {
- PackageInfo packageInfo =
- mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
- if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
+ if (packageInfo != null
+ && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return true;
}
} catch (PackageManager.NameNotFoundException e) {
@@ -1872,7 +1923,7 @@ public class AccountManagerService
&& hasExplicitlyGrantedPermission(account, authTokenType);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
- + callerUid + ", account " + account
+ + callerUid + ", " + account
+ ": is authenticator? " + fromAuthenticator
+ ", has explicit permission? " + hasExplicitGrants);
}
@@ -1884,7 +1935,7 @@ public class AccountManagerService
mAuthenticatorCache.getAllServices()) {
if (serviceInfo.type.type.equals(accountType)) {
return (serviceInfo.uid == callingUid) ||
- (mContext.getPackageManager().checkSignatures(serviceInfo.uid, callingUid)
+ (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
== PackageManager.SIGNATURE_MATCH);
}
}
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
index c6515672edf8..4d3769a8f966 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -44,9 +44,16 @@ public class AuthenticatorDescription implements Parcelable {
/** The package name that can be used to lookup the resources from above. */
final public String packageName;
- /** A constructor for a full AuthenticatorDescription */
+ /** Authenticator handles its own token caching and permission screen
+ * @hide
+ */
+ final public boolean customTokens;
+
+ /** A constructor for a full AuthenticatorDescription
+ * @hide
+ */
public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
- int smallIconId, int prefId) {
+ int smallIconId, int prefId, boolean customTokens) {
if (type == null) throw new IllegalArgumentException("type cannot be null");
if (packageName == null) throw new IllegalArgumentException("packageName cannot be null");
this.type = type;
@@ -55,6 +62,12 @@ public class AuthenticatorDescription implements Parcelable {
this.iconId = iconId;
this.smallIconId = smallIconId;
this.accountPreferencesId = prefId;
+ this.customTokens = customTokens;
+ }
+
+ public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
+ int smallIconId, int prefId) {
+ this(type, packageName, labelId, iconId, smallIconId, prefId, false);
}
/**
@@ -74,6 +87,7 @@ public class AuthenticatorDescription implements Parcelable {
this.iconId = 0;
this.smallIconId = 0;
this.accountPreferencesId = 0;
+ this.customTokens = false;
}
private AuthenticatorDescription(Parcel source) {
@@ -83,6 +97,7 @@ public class AuthenticatorDescription implements Parcelable {
this.iconId = source.readInt();
this.smallIconId = source.readInt();
this.accountPreferencesId = source.readInt();
+ this.customTokens = source.readByte() == 1;
}
/** @inheritDoc */
@@ -115,6 +130,7 @@ public class AuthenticatorDescription implements Parcelable {
dest.writeInt(iconId);
dest.writeInt(smallIconId);
dest.writeInt(accountPreferencesId);
+ dest.writeByte((byte) (customTokens ? 1 : 0));
}
/** Used to create the object from a parcel. */