[DO NOT MERGE] While-list apps to access account if already saw it
Sync adapters without an account access cannot run until the user approves the account access (for the case the account access is not allowed by other policy such as being singed with the same cert as the authenticator). However, if the sync adapter package already got the account from another app which means it already saw the account we white-list the sync adapter app to access the account as it already saw it - the bird is out of the cage. bug:31162498 Change-Id: I2b72f3b0d6307561ed68db2f2e9c900b15e8d098
This commit is contained in:
committed by
Svetoslav Ganov
parent
220aadbf9f
commit
91d37f43c7
@@ -63,6 +63,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \
|
||||
core/java/android/accounts/IAccountManager.aidl \
|
||||
core/java/android/accounts/IAccountManagerResponse.aidl \
|
||||
core/java/android/accounts/IAccountAccessTracker.aidl \
|
||||
core/java/android/accounts/IAccountAuthenticator.aidl \
|
||||
core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
|
||||
core/java/android/app/IActivityContainer.aidl \
|
||||
|
||||
@@ -16,9 +16,17 @@
|
||||
|
||||
package android.accounts;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
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
|
||||
@@ -26,8 +34,14 @@ import android.text.TextUtils;
|
||||
* suitable for use as the key of a {@link java.util.Map}
|
||||
*/
|
||||
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 type;
|
||||
private final @Nullable IAccountAccessTracker mAccessTracker;
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
@@ -44,6 +58,20 @@ public class Account implements Parcelable {
|
||||
}
|
||||
|
||||
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)) {
|
||||
throw new IllegalArgumentException("the name must not be empty: " + name);
|
||||
}
|
||||
@@ -52,11 +80,29 @@ public class Account implements Parcelable {
|
||||
}
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.mAccessTracker = accessTracker;
|
||||
}
|
||||
|
||||
public Account(Parcel in) {
|
||||
this.name = 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() {
|
||||
@@ -66,6 +112,7 @@ public class Account implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(name);
|
||||
dest.writeString(type);
|
||||
dest.writeStrongInterface(mAccessTracker);
|
||||
}
|
||||
|
||||
public static final Creator<Account> CREATOR = new Creator<Account>() {
|
||||
|
||||
@@ -18,7 +18,6 @@ package android.accounts;
|
||||
|
||||
import static android.Manifest.permission.GET_ACCOUNTS;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.Size;
|
||||
@@ -179,6 +178,14 @@ public class AccountManager {
|
||||
*/
|
||||
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
|
||||
* from {@link #getAuthToken} and friends.
|
||||
@@ -268,13 +275,13 @@ public class AccountManager {
|
||||
public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
|
||||
|
||||
/**
|
||||
* Token for the special case where a UID has access only to an account
|
||||
* but no authenticator specific auth tokens.
|
||||
* Token type for the special case where a UID has access only to an account
|
||||
* but no authenticator specific auth token types.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String ACCOUNT_ACCESS_TOKEN =
|
||||
"com.android.abbfd278-af8b-415d-af8b-7571d5dab133";
|
||||
public static final String ACCOUNT_ACCESS_TOKEN_TYPE =
|
||||
"com.android.AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE";
|
||||
|
||||
private final Context mContext;
|
||||
private final IAccountManager mService;
|
||||
@@ -814,7 +821,9 @@ public class AccountManager {
|
||||
public Account bundleToResult(Bundle bundle) throws AuthenticatorException {
|
||||
String name = bundle.getString(KEY_ACCOUNT_NAME);
|
||||
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();
|
||||
}
|
||||
@@ -2270,6 +2279,7 @@ public class AccountManager {
|
||||
result.putString(KEY_ACCOUNT_NAME, null);
|
||||
result.putString(KEY_ACCOUNT_TYPE, null);
|
||||
result.putString(KEY_AUTHTOKEN, null);
|
||||
result.putBinder(KEY_ACCOUNT_ACCESS_TRACKER, null);
|
||||
try {
|
||||
mResponse.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
@@ -2295,9 +2305,13 @@ public class AccountManager {
|
||||
public void onResult(Bundle value) throws RemoteException {
|
||||
Account account = new Account(
|
||||
value.getString(KEY_ACCOUNT_NAME),
|
||||
value.getString(KEY_ACCOUNT_TYPE));
|
||||
mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions,
|
||||
mActivity, mMyCallback, mHandler);
|
||||
value.getString(KEY_ACCOUNT_TYPE),
|
||||
IAccountAccessTracker.Stub.asInterface(
|
||||
value.getBinder(
|
||||
KEY_ACCOUNT_ACCESS_TRACKER)));
|
||||
mFuture = getAuthToken(account, mAuthTokenType,
|
||||
mLoginOptions, mActivity, mMyCallback,
|
||||
mHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2344,7 +2358,9 @@ public class AccountManager {
|
||||
setException(new AuthenticatorException("account not in result"));
|
||||
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;
|
||||
getAuthToken(account, mAuthTokenType, null /* options */, mActivity,
|
||||
mMyCallback, mHandler);
|
||||
|
||||
@@ -27,6 +27,21 @@ import android.os.RemoteCallback;
|
||||
*/
|
||||
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.
|
||||
* The provided callback will be invoked with a {@link android.os.Bundle}
|
||||
@@ -38,7 +53,24 @@ public abstract class AccountManagerInternal {
|
||||
* @param userId Concrete user id for which to request.
|
||||
* @param callback A callback for receiving the result.
|
||||
*/
|
||||
public abstract void requestAccountAccess(@NonNull Account account,
|
||||
public abstract void requestAccountAccess(@NonNull Account account,
|
||||
@NonNull String packageName, @IntRange(from = 0) int userId,
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
mAuthTokenType, callback, null);
|
||||
}
|
||||
|
||||
26
core/java/android/accounts/IAccountAccessTracker.aidl
Normal file
26
core/java/android/accounts/IAccountAccessTracker.aidl
Normal 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();
|
||||
}
|
||||
@@ -1877,6 +1877,7 @@ public abstract class ContentResolver {
|
||||
if (extras != null) {
|
||||
String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
|
||||
if (!TextUtils.isEmpty(accountName)) {
|
||||
// TODO: No references to Google in AOSP
|
||||
account = new Account(accountName, "com.google");
|
||||
}
|
||||
extras.remove(SYNC_EXTRAS_ACCOUNT);
|
||||
|
||||
@@ -26,12 +26,14 @@ import android.accounts.AccountManagerInternal;
|
||||
import android.accounts.AuthenticatorDescription;
|
||||
import android.accounts.CantAddAccountActivity;
|
||||
import android.accounts.GrantCredentialsPermissionActivity;
|
||||
import android.accounts.IAccountAccessTracker;
|
||||
import android.accounts.IAccountAuthenticator;
|
||||
import android.accounts.IAccountAuthenticatorResponse;
|
||||
import android.accounts.IAccountManager;
|
||||
import android.accounts.IAccountManagerResponse;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.app.ActivityThread;
|
||||
@@ -85,7 +87,6 @@ import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
@@ -125,6 +126,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@@ -283,7 +285,7 @@ public class AccountManagerService
|
||||
private final Object cacheLock = new Object();
|
||||
/** protected by the {@link #cacheLock} */
|
||||
private final HashMap<String, Account[]> accountCache =
|
||||
new LinkedHashMap<String, Account[]>();
|
||||
new LinkedHashMap<>();
|
||||
/** protected by the {@link #cacheLock} */
|
||||
private final HashMap<Account, HashMap<String, String>> userDataCache =
|
||||
new HashMap<Account, HashMap<String, String>>();
|
||||
@@ -322,6 +324,8 @@ public class AccountManagerService
|
||||
|
||||
private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
|
||||
private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
|
||||
private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
|
||||
mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
|
||||
private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
|
||||
@@ -502,7 +506,7 @@ public class AccountManagerService
|
||||
if (!checkAccess || hasAccountAccess(account, packageName,
|
||||
UserHandle.getUserHandleForUid(uid))) {
|
||||
cancelNotification(getCredentialPermissionNotificationId(account,
|
||||
AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
|
||||
AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
|
||||
UserHandle.getUserHandleForUid(uid));
|
||||
}
|
||||
}
|
||||
@@ -695,7 +699,8 @@ public class AccountManagerService
|
||||
final ArrayList<String> accountNames = cur.getValue();
|
||||
final Account[] accountsForType = new Account[accountNames.size()];
|
||||
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);
|
||||
}
|
||||
@@ -1505,6 +1510,8 @@ public class AccountManagerService
|
||||
Bundle result = new Bundle();
|
||||
result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
|
||||
result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
|
||||
result.putBinder(AccountManager.KEY_ACCOUNT_ACCESS_TRACKER,
|
||||
resultingAccount.getAccessTracker().asBinder());
|
||||
try {
|
||||
response.onResult(result);
|
||||
} catch (RemoteException e) {
|
||||
@@ -1857,7 +1864,7 @@ public class AccountManagerService
|
||||
for (Pair<Pair<Account, String>, Integer> key
|
||||
: accounts.credentialsPermissionNotificationIds.keySet()) {
|
||||
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;
|
||||
mMessageHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
|
||||
account, uid, false));
|
||||
@@ -3457,22 +3464,36 @@ public class AccountManagerService
|
||||
Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
|
||||
|
||||
try {
|
||||
|
||||
final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
|
||||
// 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.
|
||||
if (permissionIsGranted(account, null, uid, userId)) {
|
||||
return true;
|
||||
}
|
||||
// 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.
|
||||
return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
|
||||
|| checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
|
||||
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
|
||||
// is trusted by the authenticator, hence it is fine to access the account.
|
||||
if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
|
||||
return true;
|
||||
}
|
||||
// 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.
|
||||
return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
|
||||
|| checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
|
||||
}
|
||||
|
||||
private boolean checkUidPermission(String permission, int uid, String opPackageName) {
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
@@ -3548,7 +3569,7 @@ public class AccountManagerService
|
||||
|
||||
private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
|
||||
cancelNotification(getCredentialPermissionNotificationId(account,
|
||||
AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
|
||||
AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
|
||||
UserHandle.getUserHandleForUid(uid));
|
||||
if (callback != null) {
|
||||
Bundle result = new Bundle();
|
||||
@@ -3556,7 +3577,7 @@ public class AccountManagerService
|
||||
callback.sendResult(result);
|
||||
}
|
||||
}
|
||||
}), AccountManager.ACCOUNT_ACCESS_TOKEN, false);
|
||||
}), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -5610,6 +5631,12 @@ public class AccountManagerService
|
||||
|
||||
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 {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
|
||||
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) {
|
||||
@@ -5683,7 +5717,7 @@ public class AccountManagerService
|
||||
if (accountsForType != null) {
|
||||
System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
|
||||
}
|
||||
newAccountsForType[oldLength] = account;
|
||||
newAccountsForType[oldLength] = new Account(account, new AccountAccessTracker());
|
||||
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 {
|
||||
@Override
|
||||
public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
|
||||
@@ -5948,7 +6009,8 @@ public class AccountManagerService
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasAccountAccess(account, packageName, new UserHandle(userId))) {
|
||||
if (AccountManagerService.this.hasAccountAccess(account, packageName,
|
||||
new UserHandle(userId))) {
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
|
||||
callback.sendResult(result);
|
||||
@@ -5964,7 +6026,22 @@ public class AccountManagerService
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ public final class ContentService extends IContentService.Stub {
|
||||
SyncManager syncManager = getSyncManager();
|
||||
if (syncManager != null) {
|
||||
syncManager.scheduleSync(account, userId, uId, authority, extras,
|
||||
false /* onlyThoseWithUnkownSyncableState */);
|
||||
SyncStorageEngine.AuthorityInfo.UNDEFINED);
|
||||
}
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
@@ -548,7 +548,7 @@ public final class ContentService extends IContentService.Stub {
|
||||
} else {
|
||||
syncManager.scheduleSync(
|
||||
request.getAccount(), userId, callerUid, request.getProvider(), extras,
|
||||
false /* onlyThoseWithUnknownSyncableState */);
|
||||
SyncStorageEngine.AuthorityInfo.UNDEFINED);
|
||||
}
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
|
||||
@@ -504,7 +504,7 @@ public class SyncManager {
|
||||
@Override
|
||||
public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) {
|
||||
scheduleSync(info.account, info.userId, reason, info.provider, extras,
|
||||
false);
|
||||
AuthorityInfo.UNDEFINED);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -534,7 +534,7 @@ public class SyncManager {
|
||||
if (!removed) {
|
||||
scheduleSync(null, UserHandle.USER_ALL,
|
||||
SyncOperation.REASON_SERVICE_CHANGED,
|
||||
type.authority, null, false /* onlyThoseWithUnkownSyncableState */);
|
||||
type.authority, null, AuthorityInfo.UNDEFINED);
|
||||
}
|
||||
}
|
||||
}, mSyncHandler);
|
||||
@@ -576,6 +576,15 @@ public class SyncManager {
|
||||
mAccountManager = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE);
|
||||
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(
|
||||
BatteryStats.SERVICE_NAME));
|
||||
|
||||
@@ -671,7 +680,7 @@ public class SyncManager {
|
||||
service.type.accountType, userHandle)) {
|
||||
if (!canAccessAccount(account, packageName, userId)) {
|
||||
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
|
||||
* syncs of a specific provider. Can be null. Is ignored
|
||||
* 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,
|
||||
String requestedAuthority, Bundle extras, boolean onlyThoseWithUnkownSyncableState) {
|
||||
String requestedAuthority, Bundle extras, int targetSyncState) {
|
||||
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
if (extras == null) {
|
||||
extras = new Bundle();
|
||||
@@ -888,7 +898,7 @@ public class SyncManager {
|
||||
if (result != null
|
||||
&& result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
|
||||
scheduleSync(account.account, userId, reason, authority,
|
||||
finalExtras, onlyThoseWithUnkownSyncableState);
|
||||
finalExtras, targetSyncState);
|
||||
}
|
||||
}
|
||||
));
|
||||
@@ -903,9 +913,10 @@ public class SyncManager {
|
||||
isSyncable = AuthorityInfo.SYNCABLE;
|
||||
}
|
||||
|
||||
if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
|
||||
if (targetSyncState != AuthorityInfo.UNDEFINED && targetSyncState != isSyncable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
|
||||
continue;
|
||||
}
|
||||
@@ -931,7 +942,7 @@ public class SyncManager {
|
||||
|
||||
final String owningPackage = syncAdapterInfo.componentName.getPackageName();
|
||||
|
||||
if (isSyncable < 0) {
|
||||
if (isSyncable == AuthorityInfo.NOT_INITIALIZED) {
|
||||
// Initialisation sync.
|
||||
Bundle newExtras = new Bundle();
|
||||
newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
|
||||
@@ -950,8 +961,8 @@ public class SyncManager {
|
||||
owningUid, owningPackage, reason, source,
|
||||
authority, newExtras, allowParallelSyncs)
|
||||
);
|
||||
}
|
||||
if (!onlyThoseWithUnkownSyncableState) {
|
||||
} else if (targetSyncState == AuthorityInfo.UNDEFINED
|
||||
|| targetSyncState == isSyncable) {
|
||||
if (isLoggable) {
|
||||
Slog.v(TAG, "scheduleSync:"
|
||||
+ " delay until " + delayUntil
|
||||
@@ -1076,7 +1087,7 @@ public class SyncManager {
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
|
||||
scheduleSync(account, userId, reason, authority, extras,
|
||||
false /* onlyThoseWithUnkownSyncableState */);
|
||||
AuthorityInfo.UNDEFINED);
|
||||
}
|
||||
|
||||
public SyncAdapterType[] getSyncAdapterTypes(int userId) {
|
||||
@@ -1535,7 +1546,7 @@ public class SyncManager {
|
||||
mContext.getOpPackageName());
|
||||
for (Account account : accounts) {
|
||||
scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
|
||||
true /* onlyThoseWithUnknownSyncableState */);
|
||||
AuthorityInfo.NOT_INITIALIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2714,7 +2725,8 @@ public class SyncManager {
|
||||
|
||||
if (syncTargets != null) {
|
||||
scheduleSync(syncTargets.account, syncTargets.userId,
|
||||
SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, true);
|
||||
SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
|
||||
null, AuthorityInfo.NOT_INITIALIZED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,6 +211,12 @@ public class SyncStorageEngine extends Handler {
|
||||
|
||||
public static class AuthorityInfo {
|
||||
// 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
|
||||
* initialization sync which are governed by a different set of rules to that of regular
|
||||
|
||||
Reference in New Issue
Block a user