diff options
11 files changed, 265 insertions, 47 deletions
diff --git a/Android.mk b/Android.mk index 4ca3e229d87b..2d5285438392 100644 --- a/Android.mk +++ b/Android.mk @@ -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 \ diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java index 7b83a3076db3..6c16e322e978 100644 --- a/core/java/android/accounts/Account.java +++ b/core/java/android/accounts/Account.java @@ -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>() { diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index ed08a70bfd8d..632e4b943626 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -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; @@ -180,6 +179,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); diff --git a/core/java/android/accounts/AccountManagerInternal.java b/core/java/android/accounts/AccountManagerInternal.java index d777643950e6..0ac719777e20 100644 --- a/core/java/android/accounts/AccountManagerInternal.java +++ b/core/java/android/accounts/AccountManagerInternal.java @@ -28,6 +28,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} * containing the result which will be a boolean value mapped to the @@ -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); } diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java index 8d0ce58d3358..38eab2905022 100644 --- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java +++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java @@ -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); } diff --git a/core/java/android/accounts/IAccountAccessTracker.aidl b/core/java/android/accounts/IAccountAccessTracker.aidl new file mode 100644 index 000000000000..e12b3d1e21c7 --- /dev/null +++ b/core/java/android/accounts/IAccountAccessTracker.aidl @@ -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(); +} diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b3320d6f6976..daa1b93889cc 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -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); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 2f96b20e512a..b86da67715d6 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -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); } } } diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 12955f5771d2..4e236d164a69 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -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); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 705eae677d2b..8c714789ee2b 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -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); } } diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index 8289bae8793b..069ae7394319 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -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 |