diff options
author | 2011-03-01 12:25:21 -0800 | |
---|---|---|
committer | 2011-03-01 12:25:21 -0800 | |
commit | db52ab69f22e24615eaa2e8f9845e157426d3dd6 (patch) | |
tree | 5a6bd8a3ff0e651406b05a8e610d4501cc6b004e | |
parent | 61c1bb4fef439650a3b88131c6e1360ffc03a7ed (diff) | |
parent | ef419b2e14e292bf4200a0281ce9125eda431c12 (diff) |
Merge "DO NOT MERGE Backport (with modifications ) some changes from Honeycomb, that would allow authenticators to control caching and permissions." into gingerbread
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. */ |