diff options
6 files changed, 552 insertions, 23 deletions
diff --git a/core/java/android/accounts/AccountManagerInternal.java b/core/java/android/accounts/AccountManagerInternal.java index 0ac719777e20..68c17c32fdc3 100644 --- a/core/java/android/accounts/AccountManagerInternal.java +++ b/core/java/android/accounts/AccountManagerInternal.java @@ -73,4 +73,18 @@ public abstract class AccountManagerInternal { */ public abstract void addOnAppPermissionChangeListener( @NonNull OnAppPermissionChangeListener listener); + + /** + * Backups the account access permissions. + * @param userId The user for which to backup. + * @return The backup data. + */ + public abstract byte[] backupAccountAccessPermissions(int userId); + + /** + * Restores the account access permissions. + * @param data The restore data. + * @param userId The user for which to restore. + */ + public abstract void restoreAccountAccessPermissions(byte[] data, int userId); } diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java new file mode 100644 index 000000000000..6531aef9a50c --- /dev/null +++ b/core/java/android/util/PackageUtils.java @@ -0,0 +1,96 @@ +/* + * 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.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Helper functions applicable to packages. + * @hide + */ +public final class PackageUtils { + private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + + private PackageUtils() { + /* hide constructor */ + } + + /** + * Computes the SHA256 digest of the signing cert for a package. + * @param packageManager The package manager. + * @param packageName The package for which to generate the digest. + * @param userId The user for which to generate the digest. + * @return The digest or null if the package does not exist for this user. + */ + public static @Nullable String computePackageCertSha256Digest( + @NonNull PackageManager packageManager, + @NonNull String packageName, int userId) { + final PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SIGNATURES, userId); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + return computeCertSha256Digest(packageInfo.signatures[0]); + } + + /** + * Computes the SHA256 digest of a cert. + * @param signature The signature. + * @return The digest or null if an error occurs. + */ + public static @Nullable String computeCertSha256Digest(@NonNull Signature signature) { + return computeSha256Digest(signature.toByteArray()); + } + + /** + * Computes the SHA256 digest of some data. + * @param data The data. + * @return The digest or null if an error occurs. + */ + public static @Nullable String computeSha256Digest(@NonNull byte[] data) { + MessageDigest messageDigest; + try { + messageDigest = MessageDigest.getInstance("SHA256"); + } catch (NoSuchAlgorithmException e) { + /* can't happen */ + return null; + } + + messageDigest.update(data); + + final byte[] digest = messageDigest.digest(); + final int digestLength = digest.length; + final int charCount = 2 * digestLength; + + final char[] chars = new char[charCount]; + for (int i = 0; i < digestLength; i++) { + final int byteHex = digest[i] & 0xFF; + chars[i * 2] = HEX_ARRAY[byteHex >>> 4]; + chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F]; + } + return new String(chars); + } +} diff --git a/core/java/com/android/server/backup/AccountManagerBackupHelper.java b/core/java/com/android/server/backup/AccountManagerBackupHelper.java new file mode 100644 index 000000000000..39b18c0f2c16 --- /dev/null +++ b/core/java/com/android/server/backup/AccountManagerBackupHelper.java @@ -0,0 +1,85 @@ +/* + * 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 com.android.server.backup; + +import android.accounts.AccountManagerInternal; +import android.app.backup.BlobBackupHelper; +import android.os.UserHandle; +import android.util.Slog; +import com.android.server.LocalServices; + +/** + * Helper for handling backup of account manager specific state. + */ +public class AccountManagerBackupHelper extends BlobBackupHelper { + private static final String TAG = "AccountsBackup"; + private static final boolean DEBUG = false; + + // current schema of the backup state blob + private static final int STATE_VERSION = 1; + + // key under which the account access grant state blob is committed to backup + private static final String KEY_ACCOUNT_ACCESS_GRANTS = "account_access_grants"; + + public AccountManagerBackupHelper() { + super(STATE_VERSION, KEY_ACCOUNT_ACCESS_GRANTS); + } + + @Override + protected byte[] getBackupPayload(String key) { + AccountManagerInternal am = LocalServices.getService(AccountManagerInternal.class); + if (DEBUG) { + Slog.d(TAG, "Handling backup of " + key); + } + try { + switch (key) { + case KEY_ACCOUNT_ACCESS_GRANTS: { + return am.backupAccountAccessPermissions(UserHandle.USER_SYSTEM); + } + + default: { + Slog.w(TAG, "Unexpected backup key " + key); + } + } + } catch (Exception e) { + Slog.e(TAG, "Unable to store payload " + key); + } + + return new byte[0]; + } + + @Override + protected void applyRestoredPayload(String key, byte[] payload) { + AccountManagerInternal am = LocalServices.getService(AccountManagerInternal.class); + if (DEBUG) { + Slog.d(TAG, "Handling restore of " + key); + } + try { + switch (key) { + case KEY_ACCOUNT_ACCESS_GRANTS: { + am.restoreAccountAccessPermissions(payload, UserHandle.USER_SYSTEM); + } break; + + default: { + Slog.w(TAG, "Unexpected restore key " + key); + } + } + } catch (Exception e) { + Slog.w(TAG, "Unable to restore key " + key); + } + } +} diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 9d296fa19400..537565185d9b 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -49,6 +49,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final String PERMISSION_HELPER = "permissions"; private static final String USAGE_STATS_HELPER = "usage_stats"; private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager"; + private static final String ACCOUNT_MANAGER_HELPER = "account_manager"; // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are @@ -82,6 +83,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); + addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper()); super.onBackup(oldState, data, newState); } @@ -111,6 +113,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); + addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper()); try { super.onRestore(data, appVersionCode, newState); diff --git a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java new file mode 100644 index 000000000000..f4511d28ba46 --- /dev/null +++ b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java @@ -0,0 +1,312 @@ +/* + * 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 com.android.server.accounts; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerInternal; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; +import android.util.PackageUtils; +import android.util.Xml; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.content.PackageMonitor; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Helper class for backup and restore of account access grants. + */ +public final class AccountManagerBackupHelper { + private static final String TAG = "AccountManagerBackupHelper"; + + private static final long PENDING_RESTORE_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour + + private static final String TAG_PERMISSIONS = "permissions"; + private static final String TAG_PERMISSION = "permission"; + private static final String ATTR_ACCOUNT_SHA_256 = "account-sha-256"; + private static final String ATTR_PACKAGE = "package"; + private static final String ATTR_DIGEST = "digest"; + + private static final String ACCOUNT_ACCESS_GRANTS = "" + + "SELECT " + AccountManagerService.ACCOUNTS_NAME + ", " + + AccountManagerService.GRANTS_GRANTEE_UID + + " FROM " + AccountManagerService.TABLE_ACCOUNTS + + ", " + AccountManagerService.TABLE_GRANTS + + " WHERE " + AccountManagerService.GRANTS_ACCOUNTS_ID + + "=" + AccountManagerService.ACCOUNTS_ID; + + private final Object mLock = new Object(); + + private final AccountManagerService mAccountManagerService; + private final AccountManagerInternal mAccountManagerInternal; + + @GuardedBy("mLock") + private List<PendingAppPermission> mRestorePendingAppPermissions; + + @GuardedBy("mLock") + private RestorePackageMonitor mRestorePackageMonitor; + + @GuardedBy("mLock") + private Runnable mRestoreCancelCommand; + + public AccountManagerBackupHelper(AccountManagerService accountManagerService, + AccountManagerInternal accountManagerInternal) { + mAccountManagerService = accountManagerService; + mAccountManagerInternal = accountManagerInternal; + } + + private final class PendingAppPermission { + private final @NonNull String accountDigest; + private final @NonNull String packageName; + private final @NonNull String certDigest; + private final @IntRange(from = 0) int userId; + + public PendingAppPermission(String accountDigest, String packageName, + String certDigest, int userId) { + this.accountDigest = accountDigest; + this.packageName = packageName; + this.certDigest = certDigest; + this.userId = userId; + } + + public boolean apply(PackageManager packageManager) { + Account account = null; + AccountManagerService.UserAccounts accounts = mAccountManagerService + .getUserAccounts(userId); + synchronized (accounts.cacheLock) { + for (Account[] accountsPerType : accounts.accountCache.values()) { + for (Account accountPerType : accountsPerType) { + if (accountDigest.equals(PackageUtils.computeSha256Digest( + accountPerType.name.getBytes()))) { + account = accountPerType; + break; + } + } + if (account != null) { + break; + } + } + } + if (account == null) { + return false; + } + final PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SIGNATURES, userId); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + String currentCertDigest = PackageUtils.computeCertSha256Digest( + packageInfo.signatures[0]); + if (!certDigest.equals(currentCertDigest)) { + return false; + } + final int uid = packageInfo.applicationInfo.uid; + if (!mAccountManagerInternal.hasAccountAccess(account, uid)) { + mAccountManagerService.grantAppPermission(account, + AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid); + } + return true; + } + } + + public byte[] backupAccountAccessPermissions(int userId) { + final AccountManagerService.UserAccounts accounts = mAccountManagerService + .getUserAccounts(userId); + synchronized (accounts.cacheLock) { + SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); + try ( + Cursor cursor = db.rawQuery(ACCOUNT_ACCESS_GRANTS, null); + ) { + if (cursor == null || !cursor.moveToFirst()) { + return null; + } + + final int nameColumnIdx = cursor.getColumnIndex( + AccountManagerService.ACCOUNTS_NAME); + final int uidColumnIdx = cursor.getColumnIndex( + AccountManagerService.GRANTS_GRANTEE_UID); + + ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); + try { + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + serializer.startTag(null, TAG_PERMISSIONS); + + PackageManager packageManager = mAccountManagerService.mContext + .getPackageManager(); + + do { + final String accountName = cursor.getString(nameColumnIdx); + final int uid = cursor.getInt(uidColumnIdx); + + final String[] packageNames = packageManager.getPackagesForUid(uid); + if (packageNames == null) { + continue; + } + + for (String packageName : packageNames) { + String digest = PackageUtils.computePackageCertSha256Digest( + packageManager, packageName, userId); + if (digest != null) { + serializer.startTag(null, TAG_PERMISSION); + serializer.attribute(null, ATTR_ACCOUNT_SHA_256, + PackageUtils.computeSha256Digest(accountName.getBytes())); + serializer.attribute(null, ATTR_PACKAGE, packageName); + serializer.attribute(null, ATTR_DIGEST, digest); + serializer.endTag(null, TAG_PERMISSION); + } + } + } while (cursor.moveToNext()); + + serializer.endTag(null, TAG_PERMISSIONS); + serializer.endDocument(); + serializer.flush(); + } catch (IOException e) { + Log.e(TAG, "Error backing up account access grants", e); + return null; + } + + return dataStream.toByteArray(); + } + } + } + + public void restoreAccountAccessPermissions(byte[] data, int userId) { + try { + ByteArrayInputStream dataStream = new ByteArrayInputStream(data); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(dataStream, StandardCharsets.UTF_8.name()); + PackageManager packageManager = mAccountManagerService.mContext.getPackageManager(); + + final int permissionsOuterDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, permissionsOuterDepth)) { + if (!TAG_PERMISSIONS.equals(parser.getName())) { + continue; + } + final int permissionOuterDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, permissionOuterDepth)) { + if (!TAG_PERMISSION.equals(parser.getName())) { + continue; + } + String accountDigest = parser.getAttributeValue(null, ATTR_ACCOUNT_SHA_256); + if (TextUtils.isEmpty(accountDigest)) { + XmlUtils.skipCurrentTag(parser); + } + String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); + if (TextUtils.isEmpty(packageName)) { + XmlUtils.skipCurrentTag(parser); + } + String digest = parser.getAttributeValue(null, ATTR_DIGEST); + if (TextUtils.isEmpty(digest)) { + XmlUtils.skipCurrentTag(parser); + } + + PendingAppPermission pendingAppPermission = new PendingAppPermission( + accountDigest, packageName, digest, userId); + + if (!pendingAppPermission.apply(packageManager)) { + synchronized (mLock) { + // Start watching before add pending to avoid a missed signal + if (mRestorePackageMonitor == null) { + mRestorePackageMonitor = new RestorePackageMonitor(); + mRestorePackageMonitor.register(mAccountManagerService.mContext, + mAccountManagerService.mHandler.getLooper(), true); + } + if (mRestorePendingAppPermissions == null) { + mRestorePendingAppPermissions = new ArrayList<>(); + } + mRestorePendingAppPermissions.add(pendingAppPermission); + } + } + } + } + + // Make sure we eventually prune the in-memory pending restores + mRestoreCancelCommand = new CancelRestoreCommand(); + mAccountManagerService.mHandler.postDelayed(mRestoreCancelCommand, + PENDING_RESTORE_TIMEOUT_MILLIS); + } catch (XmlPullParserException | IOException e) { + Log.e(TAG, "Error restoring app permissions", e); + } + } + + private final class RestorePackageMonitor extends PackageMonitor { + @Override + public void onPackageAdded(String packageName, int uid) { + synchronized (mLock) { + if (mRestorePendingAppPermissions == null) { + return; + } + if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) { + return; + } + final int count = mRestorePendingAppPermissions.size(); + for (int i = count - 1; i >= 0; i--) { + PendingAppPermission pendingAppPermission = + mRestorePendingAppPermissions.get(i); + if (!pendingAppPermission.packageName.equals(packageName)) { + continue; + } + if (pendingAppPermission.apply( + mAccountManagerService.mContext.getPackageManager())) { + mRestorePendingAppPermissions.remove(i); + } + } + if (mRestorePendingAppPermissions.isEmpty() + && mRestoreCancelCommand != null) { + mAccountManagerService.mHandler.removeCallbacks(mRestoreCancelCommand); + mRestoreCancelCommand.run(); + mRestoreCancelCommand = null; + } + } + } + } + + private final class CancelRestoreCommand implements Runnable { + @Override + public void run() { + synchronized (mLock) { + mRestorePendingAppPermissions = null; + if (mRestorePackageMonitor != null) { + mRestorePackageMonitor.unregister(); + mRestorePackageMonitor = null; + } + } + } + } +} diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index a275773dec74..a72e3ab6ad62 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -89,12 +89,14 @@ import android.os.UserManager; import android.os.storage.StorageManager; import android.text.TextUtils; import android.util.Log; +import android.util.PackageUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.util.ArrayUtils; @@ -157,13 +159,6 @@ public class AccountManagerService } @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { - mService.systemReady(); - } - } - - @Override public void onUnlockUser(int userHandle) { mService.onUnlockUser(userHandle); } @@ -176,13 +171,13 @@ public class AccountManagerService private static final int MAX_DEBUG_DB_SIZE = 64; - private final Context mContext; + final Context mContext; private final PackageManager mPackageManager; private final AppOpsManager mAppOpsManager; private UserManager mUserManager; - private final MessageHandler mHandler; + final MessageHandler mHandler; // Messages that can be sent on mHandler private static final int MESSAGE_TIMED_OUT = 3; @@ -190,9 +185,9 @@ public class AccountManagerService private final IAccountAuthenticatorCache mAuthenticatorCache; - private static final String TABLE_ACCOUNTS = "accounts"; - private static final String ACCOUNTS_ID = "_id"; - private static final String ACCOUNTS_NAME = "name"; + static final String TABLE_ACCOUNTS = "accounts"; + static final String ACCOUNTS_ID = "_id"; + static final String ACCOUNTS_NAME = "name"; private static final String ACCOUNTS_TYPE = "type"; private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; private static final String ACCOUNTS_PASSWORD = "password"; @@ -206,10 +201,10 @@ public class AccountManagerService private static final String AUTHTOKENS_TYPE = "type"; private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; - private static final String TABLE_GRANTS = "grants"; - private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; + static final String TABLE_GRANTS = "grants"; + static final String GRANTS_ACCOUNTS_ID = "accounts_id"; private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; - private static final String GRANTS_GRANTEE_UID = "uid"; + static final String GRANTS_GRANTEE_UID = "uid"; private static final String TABLE_EXTRAS = "extras"; private static final String EXTRAS_ID = "_id"; @@ -278,15 +273,15 @@ public class AccountManagerService static class UserAccounts { private final int userId; - private final DeDatabaseHelper openHelper; + final DeDatabaseHelper openHelper; private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> credentialsPermissionNotificationIds = new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); private final HashMap<Account, Integer> signinRequiredNotificationIds = new HashMap<Account, Integer>(); - private final Object cacheLock = new Object(); + final Object cacheLock = new Object(); /** protected by the {@link #cacheLock} */ - private final HashMap<String, Account[]> accountCache = + final HashMap<String, Account[]> accountCache = new LinkedHashMap<>(); /** protected by the {@link #cacheLock} */ private final Map<Account, Map<String, String>> userDataCache = new HashMap<>(); @@ -1060,9 +1055,6 @@ public class AccountManagerService } } - public void systemReady() { - } - private UserManager getUserManager() { if (mUserManager == null) { mUserManager = UserManager.get(mContext); @@ -4830,7 +4822,7 @@ public class AccountManagerService } } - private class MessageHandler extends Handler { + class MessageHandler extends Handler { MessageHandler(Looper looper) { super(looper); } @@ -5943,7 +5935,7 @@ public class AccountManagerService * which is in the system. This means we don't need to protect it with permissions. * @hide */ - private void grantAppPermission(Account account, String authTokenType, int uid) { + void grantAppPermission(Account account, String authTokenType, int uid) { if (account == null || authTokenType == null) { Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); return; @@ -6688,6 +6680,11 @@ public class AccountManagerService } private final class AccountManagerInternalImpl extends AccountManagerInternal { + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private AccountManagerBackupHelper mBackupHelper; + @Override public void requestAccountAccess(@NonNull Account account, @NonNull String packageName, @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) { @@ -6742,5 +6739,27 @@ public class AccountManagerService public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) { return AccountManagerService.this.hasAccountAccess(account, null, uid); } + + @Override + public byte[] backupAccountAccessPermissions(int userId) { + synchronized (mLock) { + if (mBackupHelper == null) { + mBackupHelper = new AccountManagerBackupHelper( + AccountManagerService.this, this); + } + return mBackupHelper.backupAccountAccessPermissions(userId); + } + } + + @Override + public void restoreAccountAccessPermissions(byte[] data, int userId) { + synchronized (mLock) { + if (mBackupHelper == null) { + mBackupHelper = new AccountManagerBackupHelper( + AccountManagerService.this, this); + } + mBackupHelper.restoreAccountAccessPermissions(data, userId); + } + } } } |