AccountManagerBackupHelper.java revision 8cd927d8ea08cba89ac02dfd50ed42b486bd7f53
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.accounts; 18 19import android.accounts.Account; 20import android.accounts.AccountManager; 21import android.accounts.AccountManagerInternal; 22import android.annotation.IntRange; 23import android.annotation.NonNull; 24import android.content.pm.PackageInfo; 25import android.content.pm.PackageManager; 26import android.database.Cursor; 27import android.database.sqlite.SQLiteDatabase; 28import android.os.UserHandle; 29import android.text.TextUtils; 30import android.util.Log; 31import android.util.PackageUtils; 32import android.util.Pair; 33import android.util.Xml; 34import com.android.internal.annotations.GuardedBy; 35import com.android.internal.content.PackageMonitor; 36import com.android.internal.util.FastXmlSerializer; 37import com.android.internal.util.XmlUtils; 38import com.android.server.accounts.AccountsDb.DeDatabaseHelper; 39 40import org.xmlpull.v1.XmlPullParser; 41import org.xmlpull.v1.XmlPullParserException; 42import org.xmlpull.v1.XmlSerializer; 43 44import java.io.ByteArrayInputStream; 45import java.io.ByteArrayOutputStream; 46import java.io.IOException; 47import java.nio.charset.StandardCharsets; 48import java.util.ArrayList; 49import java.util.List; 50 51/** 52 * Helper class for backup and restore of account access grants. 53 */ 54public final class AccountManagerBackupHelper { 55 private static final String TAG = "AccountManagerBackupHelper"; 56 57 private static final long PENDING_RESTORE_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour 58 59 private static final String TAG_PERMISSIONS = "permissions"; 60 private static final String TAG_PERMISSION = "permission"; 61 private static final String ATTR_ACCOUNT_SHA_256 = "account-sha-256"; 62 private static final String ATTR_PACKAGE = "package"; 63 private static final String ATTR_DIGEST = "digest"; 64 65 private final Object mLock = new Object(); 66 67 private final AccountManagerService mAccountManagerService; 68 private final AccountManagerInternal mAccountManagerInternal; 69 70 @GuardedBy("mLock") 71 private List<PendingAppPermission> mRestorePendingAppPermissions; 72 73 @GuardedBy("mLock") 74 private RestorePackageMonitor mRestorePackageMonitor; 75 76 @GuardedBy("mLock") 77 private Runnable mRestoreCancelCommand; 78 79 public AccountManagerBackupHelper(AccountManagerService accountManagerService, 80 AccountManagerInternal accountManagerInternal) { 81 mAccountManagerService = accountManagerService; 82 mAccountManagerInternal = accountManagerInternal; 83 } 84 85 private final class PendingAppPermission { 86 private final @NonNull String accountDigest; 87 private final @NonNull String packageName; 88 private final @NonNull String certDigest; 89 private final @IntRange(from = 0) int userId; 90 91 public PendingAppPermission(String accountDigest, String packageName, 92 String certDigest, int userId) { 93 this.accountDigest = accountDigest; 94 this.packageName = packageName; 95 this.certDigest = certDigest; 96 this.userId = userId; 97 } 98 99 public boolean apply(PackageManager packageManager) { 100 Account account = null; 101 AccountManagerService.UserAccounts accounts = mAccountManagerService 102 .getUserAccounts(userId); 103 synchronized (accounts.dbLock) { 104 synchronized (accounts.cacheLock) { 105 for (Account[] accountsPerType : accounts.accountCache.values()) { 106 for (Account accountPerType : accountsPerType) { 107 if (accountDigest.equals(PackageUtils.computeSha256Digest( 108 accountPerType.name.getBytes()))) { 109 account = accountPerType; 110 break; 111 } 112 } 113 if (account != null) { 114 break; 115 } 116 } 117 } 118 } 119 if (account == null) { 120 return false; 121 } 122 final PackageInfo packageInfo; 123 try { 124 packageInfo = packageManager.getPackageInfoAsUser(packageName, 125 PackageManager.GET_SIGNATURES, userId); 126 } catch (PackageManager.NameNotFoundException e) { 127 return false; 128 } 129 String currentCertDigest = PackageUtils.computeCertSha256Digest( 130 packageInfo.signatures[0]); 131 if (!certDigest.equals(currentCertDigest)) { 132 return false; 133 } 134 final int uid = packageInfo.applicationInfo.uid; 135 if (!mAccountManagerInternal.hasAccountAccess(account, uid)) { 136 mAccountManagerService.grantAppPermission(account, 137 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid); 138 } 139 return true; 140 } 141 } 142 143 public byte[] backupAccountAccessPermissions(int userId) { 144 final AccountManagerService.UserAccounts accounts = mAccountManagerService 145 .getUserAccounts(userId); 146 synchronized (accounts.dbLock) { 147 synchronized (accounts.cacheLock) { 148 List<Pair<String, Integer>> allAccountGrants = accounts.accountsDb 149 .findAllAccountGrants(); 150 if (allAccountGrants.isEmpty()) { 151 return null; 152 } 153 try { 154 ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); 155 final XmlSerializer serializer = new FastXmlSerializer(); 156 serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); 157 serializer.startDocument(null, true); 158 serializer.startTag(null, TAG_PERMISSIONS); 159 160 PackageManager packageManager = mAccountManagerService.mContext 161 .getPackageManager(); 162 for (Pair<String, Integer> grant : allAccountGrants) { 163 final String accountName = grant.first; 164 final int uid = grant.second; 165 166 final String[] packageNames = packageManager.getPackagesForUid(uid); 167 if (packageNames == null) { 168 continue; 169 } 170 171 for (String packageName : packageNames) { 172 String digest = PackageUtils.computePackageCertSha256Digest( 173 packageManager, packageName, userId); 174 if (digest != null) { 175 serializer.startTag(null, TAG_PERMISSION); 176 serializer.attribute(null, ATTR_ACCOUNT_SHA_256, 177 PackageUtils.computeSha256Digest(accountName.getBytes())); 178 serializer.attribute(null, ATTR_PACKAGE, packageName); 179 serializer.attribute(null, ATTR_DIGEST, digest); 180 serializer.endTag(null, TAG_PERMISSION); 181 } 182 } 183 } 184 serializer.endTag(null, TAG_PERMISSIONS); 185 serializer.endDocument(); 186 serializer.flush(); 187 return dataStream.toByteArray(); 188 } catch (IOException e) { 189 Log.e(TAG, "Error backing up account access grants", e); 190 return null; 191 } 192 } 193 } 194 } 195 196 public void restoreAccountAccessPermissions(byte[] data, int userId) { 197 try { 198 ByteArrayInputStream dataStream = new ByteArrayInputStream(data); 199 XmlPullParser parser = Xml.newPullParser(); 200 parser.setInput(dataStream, StandardCharsets.UTF_8.name()); 201 PackageManager packageManager = mAccountManagerService.mContext.getPackageManager(); 202 203 final int permissionsOuterDepth = parser.getDepth(); 204 while (XmlUtils.nextElementWithin(parser, permissionsOuterDepth)) { 205 if (!TAG_PERMISSIONS.equals(parser.getName())) { 206 continue; 207 } 208 final int permissionOuterDepth = parser.getDepth(); 209 while (XmlUtils.nextElementWithin(parser, permissionOuterDepth)) { 210 if (!TAG_PERMISSION.equals(parser.getName())) { 211 continue; 212 } 213 String accountDigest = parser.getAttributeValue(null, ATTR_ACCOUNT_SHA_256); 214 if (TextUtils.isEmpty(accountDigest)) { 215 XmlUtils.skipCurrentTag(parser); 216 } 217 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); 218 if (TextUtils.isEmpty(packageName)) { 219 XmlUtils.skipCurrentTag(parser); 220 } 221 String digest = parser.getAttributeValue(null, ATTR_DIGEST); 222 if (TextUtils.isEmpty(digest)) { 223 XmlUtils.skipCurrentTag(parser); 224 } 225 226 PendingAppPermission pendingAppPermission = new PendingAppPermission( 227 accountDigest, packageName, digest, userId); 228 229 if (!pendingAppPermission.apply(packageManager)) { 230 synchronized (mLock) { 231 // Start watching before add pending to avoid a missed signal 232 if (mRestorePackageMonitor == null) { 233 mRestorePackageMonitor = new RestorePackageMonitor(); 234 mRestorePackageMonitor.register(mAccountManagerService.mContext, 235 mAccountManagerService.mHandler.getLooper(), true); 236 } 237 if (mRestorePendingAppPermissions == null) { 238 mRestorePendingAppPermissions = new ArrayList<>(); 239 } 240 mRestorePendingAppPermissions.add(pendingAppPermission); 241 } 242 } 243 } 244 } 245 246 // Make sure we eventually prune the in-memory pending restores 247 mRestoreCancelCommand = new CancelRestoreCommand(); 248 mAccountManagerService.mHandler.postDelayed(mRestoreCancelCommand, 249 PENDING_RESTORE_TIMEOUT_MILLIS); 250 } catch (XmlPullParserException | IOException e) { 251 Log.e(TAG, "Error restoring app permissions", e); 252 } 253 } 254 255 private final class RestorePackageMonitor extends PackageMonitor { 256 @Override 257 public void onPackageAdded(String packageName, int uid) { 258 synchronized (mLock) { 259 if (mRestorePendingAppPermissions == null) { 260 return; 261 } 262 if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) { 263 return; 264 } 265 final int count = mRestorePendingAppPermissions.size(); 266 for (int i = count - 1; i >= 0; i--) { 267 PendingAppPermission pendingAppPermission = 268 mRestorePendingAppPermissions.get(i); 269 if (!pendingAppPermission.packageName.equals(packageName)) { 270 continue; 271 } 272 if (pendingAppPermission.apply( 273 mAccountManagerService.mContext.getPackageManager())) { 274 mRestorePendingAppPermissions.remove(i); 275 } 276 } 277 if (mRestorePendingAppPermissions.isEmpty() 278 && mRestoreCancelCommand != null) { 279 mAccountManagerService.mHandler.removeCallbacks(mRestoreCancelCommand); 280 mRestoreCancelCommand.run(); 281 mRestoreCancelCommand = null; 282 } 283 } 284 } 285 } 286 287 private final class CancelRestoreCommand implements Runnable { 288 @Override 289 public void run() { 290 synchronized (mLock) { 291 mRestorePendingAppPermissions = null; 292 if (mRestorePackageMonitor != null) { 293 mRestorePackageMonitor.unregister(); 294 mRestorePackageMonitor = null; 295 } 296 } 297 } 298 } 299} 300