Merge "DO NOT MERGE Backport (with modifications ) some changes from Honeycomb, that would allow authenticators to control caching and permissions." into gingerbread
This commit is contained in:
committed by
Android (Google) Code Review
commit
db52ab69f2
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user