[DO NOT MERGE] While-list apps to access account if already saw it am: 91d37f43c7

am: f56f70365d

Change-Id: I3a553a7efe34feb9fbfa5d5c504874a2780bf132
This commit is contained in:
Svet Ganov
2016-09-09 02:42:23 +00:00
committed by android-build-merger
11 changed files with 265 additions and 47 deletions

View File

@@ -63,6 +63,7 @@ LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \ core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
core/java/android/accounts/IAccountManager.aidl \ core/java/android/accounts/IAccountManager.aidl \
core/java/android/accounts/IAccountManagerResponse.aidl \ core/java/android/accounts/IAccountManagerResponse.aidl \
core/java/android/accounts/IAccountAccessTracker.aidl \
core/java/android/accounts/IAccountAuthenticator.aidl \ core/java/android/accounts/IAccountAuthenticator.aidl \
core/java/android/accounts/IAccountAuthenticatorResponse.aidl \ core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
core/java/android/app/IActivityContainer.aidl \ core/java/android/app/IActivityContainer.aidl \

View File

@@ -16,9 +16,17 @@
package android.accounts; package android.accounts;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcelable; import android.os.Parcelable;
import android.os.Parcel; import android.os.Parcel;
import android.os.RemoteException;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.util.Set;
/** /**
* Value type that represents an Account in the {@link AccountManager}. This object is * Value type that represents an Account in the {@link AccountManager}. This object is
@@ -26,8 +34,14 @@ import android.text.TextUtils;
* suitable for use as the key of a {@link java.util.Map} * suitable for use as the key of a {@link java.util.Map}
*/ */
public class Account implements Parcelable { public class Account implements Parcelable {
private static final String TAG = "Account";
@GuardedBy("sAccessedAccounts")
private static final Set<Account> sAccessedAccounts = new ArraySet<>();
public final String name; public final String name;
public final String type; public final String type;
private final @Nullable IAccountAccessTracker mAccessTracker;
public boolean equals(Object o) { public boolean equals(Object o) {
if (o == this) return true; if (o == this) return true;
@@ -44,6 +58,20 @@ public class Account implements Parcelable {
} }
public Account(String name, String type) { public Account(String name, String type) {
this(name, type, null);
}
/**
* @hide
*/
public Account(@NonNull Account other, @Nullable IAccountAccessTracker accessTracker) {
this(other.name, other.type, accessTracker);
}
/**
* @hide
*/
public Account(String name, String type, IAccountAccessTracker accessTracker) {
if (TextUtils.isEmpty(name)) { if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException("the name must not be empty: " + name); throw new IllegalArgumentException("the name must not be empty: " + name);
} }
@@ -52,11 +80,29 @@ public class Account implements Parcelable {
} }
this.name = name; this.name = name;
this.type = type; this.type = type;
this.mAccessTracker = accessTracker;
} }
public Account(Parcel in) { public Account(Parcel in) {
this.name = in.readString(); this.name = in.readString();
this.type = in.readString(); this.type = in.readString();
this.mAccessTracker = IAccountAccessTracker.Stub.asInterface(in.readStrongBinder());
if (mAccessTracker != null) {
synchronized (sAccessedAccounts) {
if (sAccessedAccounts.add(this)) {
try {
mAccessTracker.onAccountAccessed();
} catch (RemoteException e) {
Log.e(TAG, "Error noting account access", e);
}
}
}
}
}
/** @hide */
public IAccountAccessTracker getAccessTracker() {
return mAccessTracker;
} }
public int describeContents() { public int describeContents() {
@@ -66,6 +112,7 @@ public class Account implements Parcelable {
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name); dest.writeString(name);
dest.writeString(type); dest.writeString(type);
dest.writeStrongInterface(mAccessTracker);
} }
public static final Creator<Account> CREATOR = new Creator<Account>() { public static final Creator<Account> CREATOR = new Creator<Account>() {

View File

@@ -18,7 +18,6 @@ package android.accounts;
import static android.Manifest.permission.GET_ACCOUNTS; import static android.Manifest.permission.GET_ACCOUNTS;
import android.annotation.IntRange;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.RequiresPermission; import android.annotation.RequiresPermission;
import android.annotation.Size; import android.annotation.Size;
@@ -179,6 +178,14 @@ public class AccountManager {
*/ */
public static final String KEY_ACCOUNT_TYPE = "accountType"; public static final String KEY_ACCOUNT_TYPE = "accountType";
/**
* Bundle key used for the {@link IAccountAccessTracker} account access tracker
* used for noting the account was accessed when unmarshalled from a parcel.
*
* @hide
*/
public static final String KEY_ACCOUNT_ACCESS_TRACKER = "accountAccessTracker";
/** /**
* Bundle key used for the auth token value in results * Bundle key used for the auth token value in results
* from {@link #getAuthToken} and friends. * from {@link #getAuthToken} and friends.
@@ -268,13 +275,13 @@ public class AccountManager {
public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
/** /**
* Token for the special case where a UID has access only to an account * Token type for the special case where a UID has access only to an account
* but no authenticator specific auth tokens. * but no authenticator specific auth token types.
* *
* @hide * @hide
*/ */
public static final String ACCOUNT_ACCESS_TOKEN = public static final String ACCOUNT_ACCESS_TOKEN_TYPE =
"com.android.abbfd278-af8b-415d-af8b-7571d5dab133"; "com.android.AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE";
private final Context mContext; private final Context mContext;
private final IAccountManager mService; private final IAccountManager mService;
@@ -814,7 +821,9 @@ public class AccountManager {
public Account bundleToResult(Bundle bundle) throws AuthenticatorException { public Account bundleToResult(Bundle bundle) throws AuthenticatorException {
String name = bundle.getString(KEY_ACCOUNT_NAME); String name = bundle.getString(KEY_ACCOUNT_NAME);
String type = bundle.getString(KEY_ACCOUNT_TYPE); String type = bundle.getString(KEY_ACCOUNT_TYPE);
return new Account(name, type); IAccountAccessTracker tracker = IAccountAccessTracker.Stub.asInterface(
bundle.getBinder(KEY_ACCOUNT_ACCESS_TRACKER));
return new Account(name, type, tracker);
} }
}.start(); }.start();
} }
@@ -2274,6 +2283,7 @@ public class AccountManager {
result.putString(KEY_ACCOUNT_NAME, null); result.putString(KEY_ACCOUNT_NAME, null);
result.putString(KEY_ACCOUNT_TYPE, null); result.putString(KEY_ACCOUNT_TYPE, null);
result.putString(KEY_AUTHTOKEN, null); result.putString(KEY_AUTHTOKEN, null);
result.putBinder(KEY_ACCOUNT_ACCESS_TRACKER, null);
try { try {
mResponse.onResult(result); mResponse.onResult(result);
} catch (RemoteException e) { } catch (RemoteException e) {
@@ -2299,9 +2309,13 @@ public class AccountManager {
public void onResult(Bundle value) throws RemoteException { public void onResult(Bundle value) throws RemoteException {
Account account = new Account( Account account = new Account(
value.getString(KEY_ACCOUNT_NAME), value.getString(KEY_ACCOUNT_NAME),
value.getString(KEY_ACCOUNT_TYPE)); value.getString(KEY_ACCOUNT_TYPE),
mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions, IAccountAccessTracker.Stub.asInterface(
mActivity, mMyCallback, mHandler); value.getBinder(
KEY_ACCOUNT_ACCESS_TRACKER)));
mFuture = getAuthToken(account, mAuthTokenType,
mLoginOptions, mActivity, mMyCallback,
mHandler);
} }
@Override @Override
@@ -2348,7 +2362,9 @@ public class AccountManager {
setException(new AuthenticatorException("account not in result")); setException(new AuthenticatorException("account not in result"));
return; return;
} }
final Account account = new Account(accountName, accountType); final IAccountAccessTracker tracker = IAccountAccessTracker.Stub.asInterface(
result.getBinder(KEY_ACCOUNT_ACCESS_TRACKER));
final Account account = new Account(accountName, accountType, tracker);
mNumAccounts = 1; mNumAccounts = 1;
getAuthToken(account, mAuthTokenType, null /* options */, mActivity, getAuthToken(account, mAuthTokenType, null /* options */, mActivity,
mMyCallback, mHandler); mMyCallback, mHandler);

View File

@@ -27,6 +27,21 @@ import android.os.RemoteCallback;
*/ */
public abstract class AccountManagerInternal { public abstract class AccountManagerInternal {
/**
* Listener for explicit UID account access grant changes.
*/
public interface OnAppPermissionChangeListener {
/**
* Called when the explicit grant state for a given UID to
* access an account changes.
*
* @param account The account
* @param uid The UID for which the grant changed
*/
public void onAppPermissionChanged(Account account, int uid);
}
/** /**
* Requests that a given package is given access to an account. * Requests that a given package is given access to an account.
* The provided callback will be invoked with a {@link android.os.Bundle} * The provided callback will be invoked with a {@link android.os.Bundle}
@@ -41,4 +56,21 @@ public abstract class AccountManagerInternal {
public abstract void requestAccountAccess(@NonNull Account account, public abstract void requestAccountAccess(@NonNull Account account,
@NonNull String packageName, @IntRange(from = 0) int userId, @NonNull String packageName, @IntRange(from = 0) int userId,
@NonNull RemoteCallback callback); @NonNull RemoteCallback callback);
/**
* Check whether the given UID has access to the account.
*
* @param account The account
* @param uid The UID
* @return Whether the UID can access the account
*/
public abstract boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid);
/**
* Adds a listener for explicit UID account access grant changes.
*
* @param listener The listener
*/
public abstract void addOnAppPermissionChangeListener(
@NonNull OnAppPermissionChangeListener listener);
} }

View File

@@ -108,7 +108,7 @@ public class GrantCredentialsPermissionActivity extends Activity implements View
} }
}; };
if (!AccountManager.ACCOUNT_ACCESS_TOKEN.equals(mAuthTokenType)) { if (!AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(mAuthTokenType)) {
AccountManager.get(this).getAuthTokenLabel(mAccount.type, AccountManager.get(this).getAuthTokenLabel(mAccount.type,
mAuthTokenType, callback, null); mAuthTokenType, callback, null);
} }

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.accounts;
/**
* Interface to track which apps accessed an account
*
* @hide
*/
oneway interface IAccountAccessTracker {
void onAccountAccessed();
}

View File

@@ -1877,6 +1877,7 @@ public abstract class ContentResolver {
if (extras != null) { if (extras != null) {
String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT); String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
if (!TextUtils.isEmpty(accountName)) { if (!TextUtils.isEmpty(accountName)) {
// TODO: No references to Google in AOSP
account = new Account(accountName, "com.google"); account = new Account(accountName, "com.google");
} }
extras.remove(SYNC_EXTRAS_ACCOUNT); extras.remove(SYNC_EXTRAS_ACCOUNT);

View File

@@ -26,12 +26,14 @@ import android.accounts.AccountManagerInternal;
import android.accounts.AuthenticatorDescription; import android.accounts.AuthenticatorDescription;
import android.accounts.CantAddAccountActivity; import android.accounts.CantAddAccountActivity;
import android.accounts.GrantCredentialsPermissionActivity; import android.accounts.GrantCredentialsPermissionActivity;
import android.accounts.IAccountAccessTracker;
import android.accounts.IAccountAuthenticator; import android.accounts.IAccountAuthenticator;
import android.accounts.IAccountAuthenticatorResponse; import android.accounts.IAccountAuthenticatorResponse;
import android.accounts.IAccountManager; import android.accounts.IAccountManager;
import android.accounts.IAccountManagerResponse; import android.accounts.IAccountManagerResponse;
import android.annotation.IntRange; import android.annotation.IntRange;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
import android.app.ActivityThread; import android.app.ActivityThread;
@@ -85,7 +87,6 @@ import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.os.storage.StorageManager; import android.os.storage.StorageManager;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
@@ -125,6 +126,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -283,7 +285,7 @@ public class AccountManagerService
private final Object cacheLock = new Object(); private final Object cacheLock = new Object();
/** protected by the {@link #cacheLock} */ /** protected by the {@link #cacheLock} */
private final HashMap<String, Account[]> accountCache = private final HashMap<String, Account[]> accountCache =
new LinkedHashMap<String, Account[]>(); new LinkedHashMap<>();
/** protected by the {@link #cacheLock} */ /** protected by the {@link #cacheLock} */
private final HashMap<Account, HashMap<String, String>> userDataCache = private final HashMap<Account, HashMap<String, String>> userDataCache =
new HashMap<Account, HashMap<String, String>>(); new HashMap<Account, HashMap<String, String>>();
@@ -322,6 +324,8 @@ public class AccountManagerService
private final SparseArray<UserAccounts> mUsers = new SparseArray<>(); private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray(); private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>(); private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{}; private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
@@ -502,7 +506,7 @@ public class AccountManagerService
if (!checkAccess || hasAccountAccess(account, packageName, if (!checkAccess || hasAccountAccess(account, packageName,
UserHandle.getUserHandleForUid(uid))) { UserHandle.getUserHandleForUid(uid))) {
cancelNotification(getCredentialPermissionNotificationId(account, cancelNotification(getCredentialPermissionNotificationId(account,
AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
UserHandle.getUserHandleForUid(uid)); UserHandle.getUserHandleForUid(uid));
} }
} }
@@ -695,7 +699,8 @@ public class AccountManagerService
final ArrayList<String> accountNames = cur.getValue(); final ArrayList<String> accountNames = cur.getValue();
final Account[] accountsForType = new Account[accountNames.size()]; final Account[] accountsForType = new Account[accountNames.size()];
for (int i = 0; i < accountsForType.length; i++) { for (int i = 0; i < accountsForType.length; i++) {
accountsForType[i] = new Account(accountNames.get(i), accountType); accountsForType[i] = new Account(accountNames.get(i), accountType,
new AccountAccessTracker());
} }
accounts.accountCache.put(accountType, accountsForType); accounts.accountCache.put(accountType, accountsForType);
} }
@@ -1505,6 +1510,8 @@ public class AccountManagerService
Bundle result = new Bundle(); Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name); result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type); result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
result.putBinder(AccountManager.KEY_ACCOUNT_ACCESS_TRACKER,
resultingAccount.getAccessTracker().asBinder());
try { try {
response.onResult(result); response.onResult(result);
} catch (RemoteException e) { } catch (RemoteException e) {
@@ -1857,7 +1864,7 @@ public class AccountManagerService
for (Pair<Pair<Account, String>, Integer> key for (Pair<Pair<Account, String>, Integer> key
: accounts.credentialsPermissionNotificationIds.keySet()) { : accounts.credentialsPermissionNotificationIds.keySet()) {
if (account.equals(key.first.first) if (account.equals(key.first.first)
&& AccountManager.ACCOUNT_ACCESS_TOKEN.equals(key.first.second)) { && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) {
final int uid = (Integer) key.second; final int uid = (Integer) key.second;
mMessageHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded( mMessageHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
account, uid, false)); account, uid, false));
@@ -3457,20 +3464,34 @@ public class AccountManagerService
Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
try { try {
final int uid = mPackageManager.getPackageUidAsUser(packageName, userId); final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
return hasAccountAccess(account, packageName, uid);
} catch (NameNotFoundException e) {
return false;
}
}
private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
int uid) {
if (packageName == null) {
String[] packageNames = mPackageManager.getPackagesForUid(uid);
if (ArrayUtils.isEmpty(packageNames)) {
return false;
}
// For app op checks related to permissions all packages in the UID
// have the same app op state, so doesn't matter which one we pick.
packageName = packageNames[0];
}
// Use null token which means any token. Having a token means the package // Use null token which means any token. Having a token means the package
// is trusted by the authenticator, hence it is fine to access the account. // is trusted by the authenticator, hence it is fine to access the account.
if (permissionIsGranted(account, null, uid, userId)) { if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
return true; return true;
} }
// In addition to the permissions required to get an auth token we also allow // In addition to the permissions required to get an auth token we also allow
// the account to be accessed by holders of the get accounts permissions. // the account to be accessed by holders of the get accounts permissions.
return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName) return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
|| checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName); || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
} catch (NameNotFoundException e) {
return false;
}
} }
private boolean checkUidPermission(String permission, int uid, String opPackageName) { private boolean checkUidPermission(String permission, int uid, String opPackageName) {
@@ -3548,7 +3569,7 @@ public class AccountManagerService
private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException { private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
cancelNotification(getCredentialPermissionNotificationId(account, cancelNotification(getCredentialPermissionNotificationId(account,
AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
UserHandle.getUserHandleForUid(uid)); UserHandle.getUserHandleForUid(uid));
if (callback != null) { if (callback != null) {
Bundle result = new Bundle(); Bundle result = new Bundle();
@@ -3556,7 +3577,7 @@ public class AccountManagerService
callback.sendResult(result); callback.sendResult(result);
} }
} }
}), AccountManager.ACCOUNT_ACCESS_TOKEN, false); }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
} }
@Override @Override
@@ -5610,6 +5631,12 @@ public class AccountManagerService
cancelAccountAccessRequestNotificationIfNeeded(account, uid, true); cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
} }
// Listeners are a final CopyOnWriteArrayList, hence no lock needed.
for (AccountManagerInternal.OnAppPermissionChangeListener listener
: mAppPermissionChangeListeners) {
mMessageHandler.post(() -> listener.onAppPermissionChanged(account, uid));
}
} }
/** /**
@@ -5642,9 +5669,16 @@ public class AccountManagerService
} finally { } finally {
db.endTransaction(); db.endTransaction();
} }
cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
new UserHandle(accounts.userId)); new UserHandle(accounts.userId));
} }
// Listeners are a final CopyOnWriteArrayList, hence no lock needed.
for (AccountManagerInternal.OnAppPermissionChangeListener listener
: mAppPermissionChangeListeners) {
mMessageHandler.post(() -> listener.onAppPermissionChanged(account, uid));
}
} }
static final private String stringArrayToString(String[] value) { static final private String stringArrayToString(String[] value) {
@@ -5683,7 +5717,7 @@ public class AccountManagerService
if (accountsForType != null) { if (accountsForType != null) {
System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
} }
newAccountsForType[oldLength] = account; newAccountsForType[oldLength] = new Account(account, new AccountAccessTracker());
accounts.accountCache.put(account.type, newAccountsForType); accounts.accountCache.put(account.type, newAccountsForType);
} }
@@ -5927,6 +5961,33 @@ public class AccountManagerService
} }
} }
private final class AccountAccessTracker extends IAccountAccessTracker.Stub {
@Override
public void onAccountAccessed() throws RemoteException {
final int uid = Binder.getCallingUid();
if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
return;
}
final int userId = UserHandle.getCallingUserId();
final long identity = Binder.clearCallingIdentity();
try {
for (Account account : getAccounts(userId, mContext.getOpPackageName())) {
IAccountAccessTracker accountTracker = account.getAccessTracker();
if (accountTracker != null && asBinder() == accountTracker.asBinder()) {
// An app just accessed the account. At this point it knows about
// it and there is not need to hide this account from the app.
if (!hasAccountAccess(account, null, uid)) {
updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
uid, true);
}
}
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
private final class AccountManagerInternalImpl extends AccountManagerInternal { private final class AccountManagerInternalImpl extends AccountManagerInternal {
@Override @Override
public void requestAccountAccess(@NonNull Account account, @NonNull String packageName, public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
@@ -5948,7 +6009,8 @@ public class AccountManagerService
return; return;
} }
if (hasAccountAccess(account, packageName, new UserHandle(userId))) { if (AccountManagerService.this.hasAccountAccess(account, packageName,
new UserHandle(userId))) {
Bundle result = new Bundle(); Bundle result = new Bundle();
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
callback.sendResult(result); callback.sendResult(result);
@@ -5964,7 +6026,22 @@ public class AccountManagerService
} }
Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback); Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
doNotification(mUsers.get(userId), account, null, intent, packageName, userId); final UserAccounts userAccounts;
synchronized (mUsers) {
userAccounts = mUsers.get(userId);
}
doNotification(userAccounts, account, null, intent, packageName, userId);
}
@Override
public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) {
// Listeners are a final CopyOnWriteArrayList, hence no lock needed.
mAppPermissionChangeListeners.add(listener);
}
@Override
public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
return AccountManagerService.this.hasAccountAccess(account, null, uid);
} }
} }
} }

View File

@@ -482,7 +482,7 @@ public final class ContentService extends IContentService.Stub {
SyncManager syncManager = getSyncManager(); SyncManager syncManager = getSyncManager();
if (syncManager != null) { if (syncManager != null) {
syncManager.scheduleSync(account, userId, uId, authority, extras, syncManager.scheduleSync(account, userId, uId, authority, extras,
false /* onlyThoseWithUnkownSyncableState */); SyncStorageEngine.AuthorityInfo.UNDEFINED);
} }
} finally { } finally {
restoreCallingIdentity(identityToken); restoreCallingIdentity(identityToken);
@@ -548,7 +548,7 @@ public final class ContentService extends IContentService.Stub {
} else { } else {
syncManager.scheduleSync( syncManager.scheduleSync(
request.getAccount(), userId, callerUid, request.getProvider(), extras, request.getAccount(), userId, callerUid, request.getProvider(), extras,
false /* onlyThoseWithUnknownSyncableState */); SyncStorageEngine.AuthorityInfo.UNDEFINED);
} }
} finally { } finally {
restoreCallingIdentity(identityToken); restoreCallingIdentity(identityToken);

View File

@@ -504,7 +504,7 @@ public class SyncManager {
@Override @Override
public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) { public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) {
scheduleSync(info.account, info.userId, reason, info.provider, extras, scheduleSync(info.account, info.userId, reason, info.provider, extras,
false); AuthorityInfo.UNDEFINED);
} }
}); });
@@ -534,7 +534,7 @@ public class SyncManager {
if (!removed) { if (!removed) {
scheduleSync(null, UserHandle.USER_ALL, scheduleSync(null, UserHandle.USER_ALL,
SyncOperation.REASON_SERVICE_CHANGED, SyncOperation.REASON_SERVICE_CHANGED,
type.authority, null, false /* onlyThoseWithUnkownSyncableState */); type.authority, null, AuthorityInfo.UNDEFINED);
} }
} }
}, mSyncHandler); }, mSyncHandler);
@@ -576,6 +576,15 @@ public class SyncManager {
mAccountManager = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE); mAccountManager = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE);
mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class); mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class);
mAccountManagerInternal.addOnAppPermissionChangeListener((Account account, int uid) -> {
// If the UID gained access to the account kick-off syncs lacking account access
if (mAccountManagerInternal.hasAccountAccess(account, uid)) {
scheduleSync(account, UserHandle.getUserId(uid),
SyncOperation.REASON_ACCOUNTS_UPDATED,
null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS);
}
});
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME)); BatteryStats.SERVICE_NAME));
@@ -671,7 +680,7 @@ public class SyncManager {
service.type.accountType, userHandle)) { service.type.accountType, userHandle)) {
if (!canAccessAccount(account, packageName, userId)) { if (!canAccessAccount(account, packageName, userId)) {
mAccountManager.updateAppPermission(account, mAccountManager.updateAppPermission(account,
AccountManager.ACCOUNT_ACCESS_TOKEN, service.uid, true); AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, service.uid, true);
} }
} }
} }
@@ -778,10 +787,11 @@ public class SyncManager {
* @param extras a Map of SyncAdapter-specific information to control * @param extras a Map of SyncAdapter-specific information to control
* syncs of a specific provider. Can be null. Is ignored * syncs of a specific provider. Can be null. Is ignored
* if the url is null. * if the url is null.
* @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state. * @param targetSyncState Only sync authorities that have the specified sync state.
* Use {@link AuthorityInfo#UNDEFINED} to sync all authorities.
*/ */
public void scheduleSync(Account requestedAccount, int userId, int reason, public void scheduleSync(Account requestedAccount, int userId, int reason,
String requestedAuthority, Bundle extras, boolean onlyThoseWithUnkownSyncableState) { String requestedAuthority, Bundle extras, int targetSyncState) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (extras == null) { if (extras == null) {
extras = new Bundle(); extras = new Bundle();
@@ -888,7 +898,7 @@ public class SyncManager {
if (result != null if (result != null
&& result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
scheduleSync(account.account, userId, reason, authority, scheduleSync(account.account, userId, reason, authority,
finalExtras, onlyThoseWithUnkownSyncableState); finalExtras, targetSyncState);
} }
} }
)); ));
@@ -903,9 +913,10 @@ public class SyncManager {
isSyncable = AuthorityInfo.SYNCABLE; isSyncable = AuthorityInfo.SYNCABLE;
} }
if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) { if (targetSyncState != AuthorityInfo.UNDEFINED && targetSyncState != isSyncable) {
continue; continue;
} }
if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) { if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
continue; continue;
} }
@@ -931,7 +942,7 @@ public class SyncManager {
final String owningPackage = syncAdapterInfo.componentName.getPackageName(); final String owningPackage = syncAdapterInfo.componentName.getPackageName();
if (isSyncable < 0) { if (isSyncable == AuthorityInfo.NOT_INITIALIZED) {
// Initialisation sync. // Initialisation sync.
Bundle newExtras = new Bundle(); Bundle newExtras = new Bundle();
newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
@@ -950,8 +961,8 @@ public class SyncManager {
owningUid, owningPackage, reason, source, owningUid, owningPackage, reason, source,
authority, newExtras, allowParallelSyncs) authority, newExtras, allowParallelSyncs)
); );
} } else if (targetSyncState == AuthorityInfo.UNDEFINED
if (!onlyThoseWithUnkownSyncableState) { || targetSyncState == isSyncable) {
if (isLoggable) { if (isLoggable) {
Slog.v(TAG, "scheduleSync:" Slog.v(TAG, "scheduleSync:"
+ " delay until " + delayUntil + " delay until " + delayUntil
@@ -1076,7 +1087,7 @@ public class SyncManager {
final Bundle extras = new Bundle(); final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
scheduleSync(account, userId, reason, authority, extras, scheduleSync(account, userId, reason, authority, extras,
false /* onlyThoseWithUnkownSyncableState */); AuthorityInfo.UNDEFINED);
} }
public SyncAdapterType[] getSyncAdapterTypes(int userId) { public SyncAdapterType[] getSyncAdapterTypes(int userId) {
@@ -1535,7 +1546,7 @@ public class SyncManager {
mContext.getOpPackageName()); mContext.getOpPackageName());
for (Account account : accounts) { for (Account account : accounts) {
scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null, scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
true /* onlyThoseWithUnknownSyncableState */); AuthorityInfo.NOT_INITIALIZED);
} }
} }
@@ -2714,7 +2725,8 @@ public class SyncManager {
if (syncTargets != null) { if (syncTargets != null) {
scheduleSync(syncTargets.account, syncTargets.userId, scheduleSync(syncTargets.account, syncTargets.userId,
SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, true); SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
null, AuthorityInfo.NOT_INITIALIZED);
} }
} }

View File

@@ -211,6 +211,12 @@ public class SyncStorageEngine extends Handler {
public static class AuthorityInfo { public static class AuthorityInfo {
// Legal values of getIsSyncable // Legal values of getIsSyncable
/**
* The syncable state is undefined.
*/
public static final int UNDEFINED = -2;
/** /**
* Default state for a newly installed adapter. An uninitialized adapter will receive an * Default state for a newly installed adapter. An uninitialized adapter will receive an
* initialization sync which are governed by a different set of rules to that of regular * initialization sync which are governed by a different set of rules to that of regular