AccountManagerBackupHelper.java revision 00de49e20ddb0d8ab5ab2d65e47bf98a040bc97b
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.cacheLock) { 104 for (Account[] accountsPerType : accounts.accountCache.values()) { 105 for (Account accountPerType : accountsPerType) { 106 if (accountDigest.equals(PackageUtils.computeSha256Digest( 107 accountPerType.name.getBytes()))) { 108 account = accountPerType; 109 break; 110 } 111 } 112 if (account != null) { 113 break; 114 } 115 } 116 } 117 if (account == null) { 118 return false; 119 } 120 final PackageInfo packageInfo; 121 try { 122 packageInfo = packageManager.getPackageInfoAsUser(packageName, 123 PackageManager.GET_SIGNATURES, userId); 124 } catch (PackageManager.NameNotFoundException e) { 125 return false; 126 } 127 String currentCertDigest = PackageUtils.computeCertSha256Digest( 128 packageInfo.signatures[0]); 129 if (!certDigest.equals(currentCertDigest)) { 130 return false; 131 } 132 final int uid = packageInfo.applicationInfo.uid; 133 if (!mAccountManagerInternal.hasAccountAccess(account, uid)) { 134 mAccountManagerService.grantAppPermission(account, 135 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid); 136 } 137 return true; 138 } 139 } 140 141 public byte[] backupAccountAccessPermissions(int userId) { 142 final AccountManagerService.UserAccounts accounts = mAccountManagerService 143 .getUserAccounts(userId); 144 synchronized (accounts.cacheLock) { 145 List<Pair<String, Integer>> allAccountGrants = accounts.accountsDb 146 .findAllAccountGrants(); 147 if (allAccountGrants.isEmpty()) { 148 return null; 149 } 150 try { 151 ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); 152 final XmlSerializer serializer = new FastXmlSerializer(); 153 serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); 154 serializer.startDocument(null, true); 155 serializer.startTag(null, TAG_PERMISSIONS); 156 157 PackageManager packageManager = mAccountManagerService.mContext.getPackageManager(); 158 for (Pair<String, Integer> grant : allAccountGrants) { 159 final String accountName = grant.first; 160 final int uid = grant.second; 161 162 final String[] packageNames = packageManager.getPackagesForUid(uid); 163 if (packageNames == null) { 164 continue; 165 } 166 167 for (String packageName : packageNames) { 168 String digest = PackageUtils.computePackageCertSha256Digest( 169 packageManager, packageName, userId); 170 if (digest != null) { 171 serializer.startTag(null, TAG_PERMISSION); 172 serializer.attribute(null, ATTR_ACCOUNT_SHA_256, 173 PackageUtils.computeSha256Digest(accountName.getBytes())); 174 serializer.attribute(null, ATTR_PACKAGE, packageName); 175 serializer.attribute(null, ATTR_DIGEST, digest); 176 serializer.endTag(null, TAG_PERMISSION); 177 } 178 } 179 } 180 serializer.endTag(null, TAG_PERMISSIONS); 181 serializer.endDocument(); 182 serializer.flush(); 183 return dataStream.toByteArray(); 184 } catch (IOException e) { 185 Log.e(TAG, "Error backing up account access grants", e); 186 return null; 187 } 188 } 189 } 190 191 public void restoreAccountAccessPermissions(byte[] data, int userId) { 192 try { 193 ByteArrayInputStream dataStream = new ByteArrayInputStream(data); 194 XmlPullParser parser = Xml.newPullParser(); 195 parser.setInput(dataStream, StandardCharsets.UTF_8.name()); 196 PackageManager packageManager = mAccountManagerService.mContext.getPackageManager(); 197 198 final int permissionsOuterDepth = parser.getDepth(); 199 while (XmlUtils.nextElementWithin(parser, permissionsOuterDepth)) { 200 if (!TAG_PERMISSIONS.equals(parser.getName())) { 201 continue; 202 } 203 final int permissionOuterDepth = parser.getDepth(); 204 while (XmlUtils.nextElementWithin(parser, permissionOuterDepth)) { 205 if (!TAG_PERMISSION.equals(parser.getName())) { 206 continue; 207 } 208 String accountDigest = parser.getAttributeValue(null, ATTR_ACCOUNT_SHA_256); 209 if (TextUtils.isEmpty(accountDigest)) { 210 XmlUtils.skipCurrentTag(parser); 211 } 212 String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); 213 if (TextUtils.isEmpty(packageName)) { 214 XmlUtils.skipCurrentTag(parser); 215 } 216 String digest = parser.getAttributeValue(null, ATTR_DIGEST); 217 if (TextUtils.isEmpty(digest)) { 218 XmlUtils.skipCurrentTag(parser); 219 } 220 221 PendingAppPermission pendingAppPermission = new PendingAppPermission( 222 accountDigest, packageName, digest, userId); 223 224 if (!pendingAppPermission.apply(packageManager)) { 225 synchronized (mLock) { 226 // Start watching before add pending to avoid a missed signal 227 if (mRestorePackageMonitor == null) { 228 mRestorePackageMonitor = new RestorePackageMonitor(); 229 mRestorePackageMonitor.register(mAccountManagerService.mContext, 230 mAccountManagerService.mHandler.getLooper(), true); 231 } 232 if (mRestorePendingAppPermissions == null) { 233 mRestorePendingAppPermissions = new ArrayList<>(); 234 } 235 mRestorePendingAppPermissions.add(pendingAppPermission); 236 } 237 } 238 } 239 } 240 241 // Make sure we eventually prune the in-memory pending restores 242 mRestoreCancelCommand = new CancelRestoreCommand(); 243 mAccountManagerService.mHandler.postDelayed(mRestoreCancelCommand, 244 PENDING_RESTORE_TIMEOUT_MILLIS); 245 } catch (XmlPullParserException | IOException e) { 246 Log.e(TAG, "Error restoring app permissions", e); 247 } 248 } 249 250 private final class RestorePackageMonitor extends PackageMonitor { 251 @Override 252 public void onPackageAdded(String packageName, int uid) { 253 synchronized (mLock) { 254 if (mRestorePendingAppPermissions == null) { 255 return; 256 } 257 if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) { 258 return; 259 } 260 final int count = mRestorePendingAppPermissions.size(); 261 for (int i = count - 1; i >= 0; i--) { 262 PendingAppPermission pendingAppPermission = 263 mRestorePendingAppPermissions.get(i); 264 if (!pendingAppPermission.packageName.equals(packageName)) { 265 continue; 266 } 267 if (pendingAppPermission.apply( 268 mAccountManagerService.mContext.getPackageManager())) { 269 mRestorePendingAppPermissions.remove(i); 270 } 271 } 272 if (mRestorePendingAppPermissions.isEmpty() 273 && mRestoreCancelCommand != null) { 274 mAccountManagerService.mHandler.removeCallbacks(mRestoreCancelCommand); 275 mRestoreCancelCommand.run(); 276 mRestoreCancelCommand = null; 277 } 278 } 279 } 280 } 281 282 private final class CancelRestoreCommand implements Runnable { 283 @Override 284 public void run() { 285 synchronized (mLock) { 286 mRestorePendingAppPermissions = null; 287 if (mRestorePackageMonitor != null) { 288 mRestorePackageMonitor.unregister(); 289 mRestorePackageMonitor = null; 290 } 291 } 292 } 293 } 294} 295