1 /* 2 * Copyright (C) 2017 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 17 package com.android.server.backup.utils; 18 19 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME; 20 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION; 21 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_MANIFEST_PACKAGE_NAME; 22 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_OLD_VERSION; 23 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_POLICY_ALLOW_APKS; 24 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT; 25 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY; 26 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED; 27 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK; 28 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE; 29 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE; 30 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH; 31 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE; 32 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION; 33 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT; 34 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH; 35 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER; 36 37 import static com.android.server.backup.BackupManagerService.DEBUG; 38 import static com.android.server.backup.BackupManagerService.MORE_DEBUG; 39 import static com.android.server.backup.BackupManagerService.TAG; 40 import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME; 41 import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION; 42 import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME; 43 import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN; 44 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; 45 46 import android.app.backup.BackupAgent; 47 import android.app.backup.BackupManagerMonitor; 48 import android.app.backup.FullBackup; 49 import android.app.backup.IBackupManagerMonitor; 50 import android.content.Context; 51 import android.content.pm.ApplicationInfo; 52 import android.content.pm.PackageInfo; 53 import android.content.pm.PackageManager; 54 import android.content.pm.PackageManagerInternal; 55 import android.content.pm.Signature; 56 import android.os.Bundle; 57 import android.os.UserHandle; 58 import android.util.Slog; 59 60 import com.android.server.backup.FileMetadata; 61 import com.android.server.backup.restore.RestorePolicy; 62 63 import java.io.ByteArrayInputStream; 64 import java.io.DataInputStream; 65 import java.io.IOException; 66 import java.io.InputStream; 67 68 /** 69 * Utility methods to read backup tar file. 70 */ 71 public class TarBackupReader { 72 private static final int TAR_HEADER_OFFSET_TYPE_CHAR = 156; 73 private static final int TAR_HEADER_LENGTH_PATH = 100; 74 private static final int TAR_HEADER_OFFSET_PATH = 0; 75 private static final int TAR_HEADER_LENGTH_PATH_PREFIX = 155; 76 private static final int TAR_HEADER_OFFSET_PATH_PREFIX = 345; 77 private static final int TAR_HEADER_LENGTH_MODE = 8; 78 private static final int TAR_HEADER_OFFSET_MODE = 100; 79 private static final int TAR_HEADER_LENGTH_MODTIME = 12; 80 private static final int TAR_HEADER_OFFSET_MODTIME = 136; 81 private static final int TAR_HEADER_LENGTH_FILESIZE = 12; 82 private static final int TAR_HEADER_OFFSET_FILESIZE = 124; 83 private static final int TAR_HEADER_LONG_RADIX = 8; 84 85 private final InputStream mInputStream; 86 private final BytesReadListener mBytesReadListener; 87 88 89 private BackupManagerMonitorEventSender mBackupManagerMonitorEventSender; 90 91 // Widget blob to be restored out-of-band. 92 private byte[] mWidgetData = null; 93 TarBackupReader(InputStream inputStream, BytesReadListener bytesReadListener, IBackupManagerMonitor monitor)94 public TarBackupReader(InputStream inputStream, BytesReadListener bytesReadListener, 95 IBackupManagerMonitor monitor) { 96 mInputStream = inputStream; 97 mBytesReadListener = bytesReadListener; 98 mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(monitor); 99 } 100 101 /** 102 * Consumes a tar file header block [sequence] and accumulates the relevant metadata. 103 */ readTarHeaders()104 public FileMetadata readTarHeaders() throws IOException { 105 byte[] block = new byte[512]; 106 FileMetadata info = null; 107 108 boolean gotHeader = readTarHeader(block); 109 if (gotHeader) { 110 try { 111 // okay, presume we're okay, and extract the various metadata 112 info = new FileMetadata(); 113 info.size = extractRadix(block, 114 TAR_HEADER_OFFSET_FILESIZE, 115 TAR_HEADER_LENGTH_FILESIZE, 116 TAR_HEADER_LONG_RADIX); 117 info.mtime = extractRadix(block, 118 TAR_HEADER_OFFSET_MODTIME, 119 TAR_HEADER_LENGTH_MODTIME, 120 TAR_HEADER_LONG_RADIX); 121 info.mode = extractRadix(block, 122 TAR_HEADER_OFFSET_MODE, 123 TAR_HEADER_LENGTH_MODE, 124 TAR_HEADER_LONG_RADIX); 125 126 info.path = extractString(block, 127 TAR_HEADER_OFFSET_PATH_PREFIX, 128 TAR_HEADER_LENGTH_PATH_PREFIX); 129 String path = extractString(block, 130 TAR_HEADER_OFFSET_PATH, 131 TAR_HEADER_LENGTH_PATH); 132 if (path.length() > 0) { 133 if (info.path.length() > 0) { 134 info.path += '/'; 135 } 136 info.path += path; 137 } 138 139 // tar link indicator field: 1 byte at offset 156 in the header. 140 int typeChar = block[TAR_HEADER_OFFSET_TYPE_CHAR]; 141 if (typeChar == 'x') { 142 // pax extended header, so we need to read that 143 gotHeader = readPaxExtendedHeader(info); 144 if (gotHeader) { 145 // and after a pax extended header comes another real header -- read 146 // that to find the real file type 147 gotHeader = readTarHeader(block); 148 } 149 if (!gotHeader) { 150 throw new IOException("Bad or missing pax header"); 151 } 152 153 typeChar = block[TAR_HEADER_OFFSET_TYPE_CHAR]; 154 } 155 156 switch (typeChar) { 157 case '0': 158 info.type = BackupAgent.TYPE_FILE; 159 break; 160 case '5': { 161 info.type = BackupAgent.TYPE_DIRECTORY; 162 if (info.size != 0) { 163 Slog.w(TAG, "Directory entry with nonzero size in header"); 164 info.size = 0; 165 } 166 break; 167 } 168 case 0: { 169 // presume EOF 170 if (MORE_DEBUG) { 171 Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 172 } 173 return null; 174 } 175 default: { 176 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 177 throw new IOException("Unknown entity type " + typeChar); 178 } 179 } 180 181 // Parse out the path 182 // 183 // first: apps/shared/unrecognized 184 if (FullBackup.SHARED_PREFIX.regionMatches(0, 185 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 186 // File in shared storage. !!! TODO: implement this. 187 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 188 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 189 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 190 if (DEBUG) { 191 Slog.i(TAG, "File in shared storage: " + info.path); 192 } 193 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 194 info.path, 0, FullBackup.APPS_PREFIX.length())) { 195 // App content! Parse out the package name and domain 196 197 // strip the apps/ prefix 198 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 199 200 // extract the package name 201 int slash = info.path.indexOf('/'); 202 if (slash < 0) { 203 throw new IOException("Illegal semantic path in " + info.path); 204 } 205 info.packageName = info.path.substring(0, slash); 206 info.path = info.path.substring(slash + 1); 207 208 // if it's a manifest or metadata payload we're done, otherwise parse 209 // out the domain into which the file will be restored 210 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) && 211 !info.path.equals(BACKUP_METADATA_FILENAME)) { 212 slash = info.path.indexOf('/'); 213 if (slash < 0) { 214 throw new IOException("Illegal semantic path in non-manifest " 215 + info.path); 216 } 217 info.domain = info.path.substring(0, slash); 218 info.path = info.path.substring(slash + 1); 219 } 220 } 221 } catch (IOException e) { 222 if (DEBUG) { 223 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 224 if (MORE_DEBUG) { 225 hexLog(block); 226 } 227 } 228 throw e; 229 } 230 } 231 return info; 232 } 233 234 /** 235 * Tries to read exactly the given number of bytes into a buffer at the stated offset. 236 * 237 * @param in - input stream to read bytes from.. 238 * @param buffer - where to write bytes to. 239 * @param offset - offset in buffer to write bytes to. 240 * @param size - number of bytes to read. 241 * @return number of bytes actually read. 242 * @throws IOException in case of an error. 243 */ readExactly(InputStream in, byte[] buffer, int offset, int size)244 private static int readExactly(InputStream in, byte[] buffer, int offset, int size) 245 throws IOException { 246 if (size <= 0) { 247 throw new IllegalArgumentException("size must be > 0"); 248 } 249 if (MORE_DEBUG) { 250 Slog.i(TAG, " ... readExactly(" + size + ") called"); 251 } 252 int soFar = 0; 253 while (soFar < size) { 254 int nRead = in.read(buffer, offset + soFar, size - soFar); 255 if (nRead <= 0) { 256 if (MORE_DEBUG) { 257 Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 258 } 259 break; 260 } 261 soFar += nRead; 262 if (MORE_DEBUG) { 263 Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar)); 264 } 265 } 266 return soFar; 267 } 268 269 /** 270 * Reads app manifest, filling version and hasApk fields in the metadata, and returns array of 271 * signatures. 272 * 273 * @param info - file metadata. 274 * @return array of signatures or null, in case of an error. 275 * @throws IOException in case of an error. 276 */ readAppManifestAndReturnSignatures(FileMetadata info)277 public Signature[] readAppManifestAndReturnSignatures(FileMetadata info) 278 throws IOException { 279 // Fail on suspiciously large manifest files 280 if (info.size > 64 * 1024) { 281 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 282 } 283 284 byte[] buffer = new byte[(int) info.size]; 285 if (MORE_DEBUG) { 286 Slog.i(TAG, 287 " readAppManifestAndReturnSignatures() looking for " + info.size + " bytes"); 288 } 289 if (readExactly(mInputStream, buffer, 0, (int) info.size) == info.size) { 290 mBytesReadListener.onBytesRead(info.size); 291 } else { 292 throw new IOException("Unexpected EOF in manifest"); 293 } 294 295 String[] str = new String[1]; 296 int offset = 0; 297 298 try { 299 offset = extractLine(buffer, offset, str); 300 int version = Integer.parseInt(str[0]); 301 if (version == BACKUP_MANIFEST_VERSION) { 302 offset = extractLine(buffer, offset, str); 303 String manifestPackage = str[0]; 304 // TODO: handle <original-package> 305 if (manifestPackage.equals(info.packageName)) { 306 offset = extractLine(buffer, offset, str); 307 info.version = Integer.parseInt(str[0]); // app version 308 offset = extractLine(buffer, offset, str); 309 // This is the platform version, which we don't use, but we parse it 310 // as a safety against corruption in the manifest. 311 Integer.parseInt(str[0]); 312 offset = extractLine(buffer, offset, str); 313 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 314 offset = extractLine(buffer, offset, str); 315 info.hasApk = str[0].equals("1"); 316 offset = extractLine(buffer, offset, str); 317 int numSigs = Integer.parseInt(str[0]); 318 if (numSigs > 0) { 319 Signature[] sigs = new Signature[numSigs]; 320 for (int i = 0; i < numSigs; i++) { 321 offset = extractLine(buffer, offset, str); 322 sigs[i] = new Signature(str[0]); 323 } 324 return sigs; 325 } else { 326 Slog.i(TAG, "Missing signature on backed-up package " + info.packageName); 327 mBackupManagerMonitorEventSender.monitorEvent( 328 LOG_EVENT_ID_MISSING_SIGNATURE, 329 null, 330 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 331 mBackupManagerMonitorEventSender.putMonitoringExtra(null, 332 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName)); 333 } 334 } else { 335 Slog.i(TAG, "Expected package " + info.packageName 336 + " but restore manifest claims " + manifestPackage); 337 Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra( 338 null, EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); 339 monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra( 340 monitoringExtras, 341 EXTRA_LOG_MANIFEST_PACKAGE_NAME, manifestPackage); 342 mBackupManagerMonitorEventSender.monitorEvent( 343 LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE, 344 null, 345 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 346 monitoringExtras); 347 } 348 } else { 349 Slog.i(TAG, "Unknown restore manifest version " + version 350 + " for package " + info.packageName); 351 Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra( 352 null, EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); 353 monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra( 354 monitoringExtras, EXTRA_LOG_EVENT_PACKAGE_VERSION, version); 355 mBackupManagerMonitorEventSender.monitorEvent( 356 BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION, 357 null, 358 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 359 monitoringExtras); 360 361 } 362 } catch (NumberFormatException e) { 363 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 364 mBackupManagerMonitorEventSender.monitorEvent( 365 BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST, 366 null, 367 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 368 mBackupManagerMonitorEventSender.putMonitoringExtra(null, 369 EXTRA_LOG_EVENT_PACKAGE_NAME, 370 info.packageName)); 371 } catch (IllegalArgumentException e) { 372 Slog.w(TAG, e.getMessage()); 373 } 374 375 return null; 376 } 377 378 /** 379 * Chooses restore policy. 380 * 381 * @param packageManager - PackageManager instance. 382 * @param allowApks - allow restore set to include apks. 383 * @param info - file metadata. 384 * @param signatures - array of signatures parsed from backup file. 385 * @param userId - ID of the user for which restore is performed. 386 * @param context - Context instance. 387 * @return a restore policy constant. 388 */ chooseRestorePolicy(PackageManager packageManager, boolean allowApks, FileMetadata info, Signature[] signatures, PackageManagerInternal pmi, int userId, Context context)389 public RestorePolicy chooseRestorePolicy(PackageManager packageManager, 390 boolean allowApks, FileMetadata info, Signature[] signatures, 391 PackageManagerInternal pmi, int userId, Context context) { 392 return chooseRestorePolicy(packageManager, allowApks, info, signatures, pmi, userId, 393 BackupEligibilityRules.forBackup(packageManager, pmi, userId, context)); 394 } 395 396 /** 397 * Chooses restore policy. 398 * 399 * @param packageManager - PackageManager instance. 400 * @param allowApks - allow restore set to include apks. 401 * @param info - file metadata. 402 * @param signatures - array of signatures parsed from backup file. 403 * @param userId - ID of the user for which restore is performed. 404 * @param eligibilityRules - {@link BackupEligibilityRules} for this operation. 405 * @return a restore policy constant. 406 */ chooseRestorePolicy(PackageManager packageManager, boolean allowApks, FileMetadata info, Signature[] signatures, PackageManagerInternal pmi, int userId, BackupEligibilityRules eligibilityRules)407 public RestorePolicy chooseRestorePolicy(PackageManager packageManager, 408 boolean allowApks, FileMetadata info, Signature[] signatures, 409 PackageManagerInternal pmi, int userId, BackupEligibilityRules eligibilityRules) { 410 if (signatures == null) { 411 return RestorePolicy.IGNORE; 412 } 413 414 RestorePolicy policy = RestorePolicy.IGNORE; 415 // Okay, got the manifest info we need... 416 try { 417 PackageInfo pkgInfo = packageManager.getPackageInfoAsUser( 418 info.packageName, PackageManager.GET_SIGNING_CERTIFICATES, userId); 419 // Fall through to IGNORE if the app explicitly disallows backup 420 final int flags = pkgInfo.applicationInfo.flags; 421 if (eligibilityRules.isAppBackupAllowed(pkgInfo.applicationInfo)) { 422 // Restore system-uid-space packages only if they have 423 // defined a custom backup agent 424 if (!UserHandle.isCore(pkgInfo.applicationInfo.uid) 425 || (pkgInfo.applicationInfo.backupAgentName != null)) { 426 // Verify signatures against any installed version; if they 427 // don't match, then we fall though and ignore the data. The 428 // signatureMatch() method explicitly ignores the signature 429 // check for packages installed on the system partition, because 430 // such packages are signed with the platform cert instead of 431 // the app developer's cert, so they're different on every 432 // device. 433 if (eligibilityRules.signaturesMatch(signatures, pkgInfo)) { 434 if ((pkgInfo.applicationInfo.flags 435 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) { 436 Slog.i(TAG, "Package has restoreAnyVersion; taking data"); 437 mBackupManagerMonitorEventSender.monitorEvent( 438 LOG_EVENT_ID_RESTORE_ANY_VERSION, 439 pkgInfo, 440 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 441 null); 442 policy = RestorePolicy.ACCEPT; 443 } else if (pkgInfo.getLongVersionCode() >= info.version) { 444 Slog.i(TAG, "Sig + version match; taking data"); 445 policy = RestorePolicy.ACCEPT; 446 mBackupManagerMonitorEventSender.monitorEvent( 447 LOG_EVENT_ID_VERSIONS_MATCH, 448 pkgInfo, 449 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 450 null); 451 } else { 452 // The data is from a newer version of the app than 453 // is presently installed. That means we can only 454 // use it if the matching apk is also supplied. 455 if (allowApks) { 456 Slog.i(TAG, "Data version " + info.version 457 + " is newer than installed " 458 + "version " 459 + pkgInfo.getLongVersionCode() 460 + " - requiring apk"); 461 policy = RestorePolicy.ACCEPT_IF_APK; 462 } else { 463 Slog.i(TAG, "Data requires newer version " 464 + info.version + "; ignoring"); 465 mBackupManagerMonitorEventSender.monitorEvent( 466 LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER, 467 pkgInfo, 468 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 469 mBackupManagerMonitorEventSender 470 .putMonitoringExtra( 471 null, 472 EXTRA_LOG_OLD_VERSION, 473 info.version)); 474 475 policy = RestorePolicy.IGNORE; 476 } 477 } 478 } else { 479 Slog.w(TAG, "Restore manifest signatures do not match " 480 + "installed application for " 481 + info.packageName); 482 mBackupManagerMonitorEventSender.monitorEvent( 483 LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH, 484 pkgInfo, 485 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 486 null); 487 } 488 } else { 489 Slog.w(TAG, "Package " + info.packageName 490 + " is system level with no agent"); 491 mBackupManagerMonitorEventSender.monitorEvent( 492 LOG_EVENT_ID_SYSTEM_APP_NO_AGENT, 493 pkgInfo, 494 LOG_EVENT_CATEGORY_AGENT, 495 null); 496 } 497 } else { 498 if (DEBUG) { 499 Slog.i(TAG, 500 "Restore manifest from " + info.packageName + " but allowBackup=false"); 501 } 502 mBackupManagerMonitorEventSender.monitorEvent( 503 LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE, 504 pkgInfo, 505 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 506 null); 507 } 508 } catch (PackageManager.NameNotFoundException e) { 509 // Okay, the target app isn't installed. We can process 510 // the restore properly only if the dataset provides the 511 // apk file and we can successfully install it. 512 if (allowApks) { 513 if (DEBUG) { 514 Slog.i(TAG, "Package " + info.packageName 515 + " not installed; requiring apk in dataset"); 516 } 517 policy = RestorePolicy.ACCEPT_IF_APK; 518 } else { 519 policy = RestorePolicy.IGNORE; 520 } 521 Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra( 522 null, 523 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); 524 monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra( 525 monitoringExtras, 526 EXTRA_LOG_POLICY_ALLOW_APKS, allowApks); 527 mBackupManagerMonitorEventSender.monitorEvent( 528 LOG_EVENT_ID_APK_NOT_INSTALLED, 529 null, 530 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 531 monitoringExtras); 532 } 533 534 if (policy == RestorePolicy.ACCEPT_IF_APK && !info.hasApk) { 535 Slog.i(TAG, "Cannot restore package " + info.packageName 536 + " without the matching .apk"); 537 mBackupManagerMonitorEventSender.monitorEvent( 538 LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK, 539 null, 540 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 541 mBackupManagerMonitorEventSender.putMonitoringExtra(null, 542 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName)); 543 } 544 545 return policy; 546 } 547 548 // Given an actual file content size, consume the post-content padding mandated 549 // by the tar format. skipTarPadding(long size)550 public void skipTarPadding(long size) throws IOException { 551 long partial = (size + 512) % 512; 552 if (partial > 0) { 553 final int needed = 512 - (int) partial; 554 if (MORE_DEBUG) { 555 Slog.i(TAG, "Skipping tar padding: " + needed + " bytes"); 556 } 557 byte[] buffer = new byte[needed]; 558 if (readExactly(mInputStream, buffer, 0, needed) == needed) { 559 mBytesReadListener.onBytesRead(needed); 560 } else { 561 throw new IOException("Unexpected EOF in padding"); 562 } 563 } 564 } 565 566 /** 567 * Read a widget metadata file, returning the restored blob. 568 */ readMetadata(FileMetadata info)569 public void readMetadata(FileMetadata info) throws IOException { 570 // Fail on suspiciously large widget dump files 571 if (info.size > 64 * 1024) { 572 throw new IOException("Metadata too big; corrupt? size=" + info.size); 573 } 574 575 byte[] buffer = new byte[(int) info.size]; 576 if (readExactly(mInputStream, buffer, 0, (int) info.size) == info.size) { 577 mBytesReadListener.onBytesRead(info.size); 578 } else { 579 throw new IOException("Unexpected EOF in widget data"); 580 } 581 582 String[] str = new String[1]; 583 int offset = extractLine(buffer, 0, str); 584 int version = Integer.parseInt(str[0]); 585 if (version == BACKUP_MANIFEST_VERSION) { 586 offset = extractLine(buffer, offset, str); 587 final String pkg = str[0]; 588 if (info.packageName.equals(pkg)) { 589 // Data checks out -- the rest of the buffer is a concatenation of 590 // binary blobs as described in the comment at writeAppWidgetData() 591 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 592 offset, buffer.length - offset); 593 DataInputStream in = new DataInputStream(bin); 594 while (bin.available() > 0) { 595 int token = in.readInt(); 596 int size = in.readInt(); 597 if (size > 64 * 1024) { 598 throw new IOException("Datum " + Integer.toHexString(token) 599 + " too big; corrupt? size=" + info.size); 600 } 601 switch (token) { 602 case BACKUP_WIDGET_METADATA_TOKEN: { 603 if (MORE_DEBUG) { 604 Slog.i(TAG, "Got widget metadata for " + info.packageName); 605 } 606 mWidgetData = new byte[size]; 607 in.read(mWidgetData); 608 break; 609 } 610 default: { 611 if (DEBUG) { 612 Slog.i(TAG, "Ignoring metadata blob " + Integer.toHexString(token) 613 + " for " + info.packageName); 614 } 615 in.skipBytes(size); 616 break; 617 } 618 } 619 } 620 } else { 621 Slog.w(TAG, 622 "Metadata mismatch: package " + info.packageName + " but widget data for " 623 + pkg); 624 625 Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(null, 626 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); 627 monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra( 628 monitoringExtras, BackupManagerMonitor.EXTRA_LOG_WIDGET_PACKAGE_NAME, pkg); 629 mBackupManagerMonitorEventSender.monitorEvent( 630 BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH, 631 null, 632 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 633 monitoringExtras); 634 } 635 } else { 636 Slog.w(TAG, "Unsupported metadata version " + version); 637 638 Bundle monitoringExtras = mBackupManagerMonitorEventSender 639 .putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME, 640 info.packageName); 641 monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(monitoringExtras, 642 EXTRA_LOG_EVENT_PACKAGE_VERSION, version); 643 mBackupManagerMonitorEventSender.monitorEvent( 644 BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION, 645 null, 646 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 647 monitoringExtras); 648 } 649 } 650 651 /** 652 * Builds a line from a byte buffer starting at 'offset'. 653 * 654 * @param buffer - where to read a line from. 655 * @param offset - offset in buffer to read a line from. 656 * @param outStr - an output parameter, the result will be put in outStr. 657 * @return the index of the next unconsumed data in the buffer. 658 * @throws IOException in case of an error. 659 */ extractLine(byte[] buffer, int offset, String[] outStr)660 private static int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 661 final int end = buffer.length; 662 if (offset >= end) { 663 throw new IOException("Incomplete data"); 664 } 665 666 int pos; 667 for (pos = offset; pos < end; pos++) { 668 byte c = buffer[pos]; 669 // at LF we declare end of line, and return the next char as the 670 // starting point for the next time through 671 if (c == '\n') { 672 break; 673 } 674 } 675 outStr[0] = new String(buffer, offset, pos - offset); 676 pos++; // may be pointing an extra byte past the end but that's okay 677 return pos; 678 } 679 readTarHeader(byte[] block)680 private boolean readTarHeader(byte[] block) throws IOException { 681 final int got = readExactly(mInputStream, block, 0, 512); 682 if (got == 0) { 683 return false; // Clean EOF 684 } 685 if (got < 512) { 686 throw new IOException("Unable to read full block header"); 687 } 688 mBytesReadListener.onBytesRead(512); 689 return true; 690 } 691 692 // overwrites 'info' fields based on the pax extended header readPaxExtendedHeader(FileMetadata info)693 private boolean readPaxExtendedHeader(FileMetadata info) 694 throws IOException { 695 // We should never see a pax extended header larger than this 696 if (info.size > 32 * 1024) { 697 Slog.w(TAG, "Suspiciously large pax header size " + info.size + " - aborting"); 698 throw new IOException("Sanity failure: pax header size " + info.size); 699 } 700 701 // read whole blocks, not just the content size 702 int numBlocks = (int) ((info.size + 511) >> 9); 703 byte[] data = new byte[numBlocks * 512]; 704 if (readExactly(mInputStream, data, 0, data.length) < data.length) { 705 throw new IOException("Unable to read full pax header"); 706 } 707 mBytesReadListener.onBytesRead(data.length); 708 709 final int contentSize = (int) info.size; 710 int offset = 0; 711 do { 712 // extract the line at 'offset' 713 int eol = offset + 1; 714 while (eol < contentSize && data[eol] != ' ') { 715 eol++; 716 } 717 if (eol >= contentSize) { 718 // error: we just hit EOD looking for the end of the size field 719 throw new IOException("Invalid pax data"); 720 } 721 // eol points to the space between the count and the key 722 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 723 int key = eol + 1; // start of key=value 724 eol = offset + linelen - 1; // trailing LF 725 int value; 726 for (value = key + 1; data[value] != '=' && value <= eol; value++) { 727 ; 728 } 729 if (value > eol) { 730 throw new IOException("Invalid pax declaration"); 731 } 732 733 // pax requires that key/value strings be in UTF-8 734 String keyStr = new String(data, key, value - key, "UTF-8"); 735 // -1 to strip the trailing LF 736 String valStr = new String(data, value + 1, eol - value - 1, "UTF-8"); 737 738 if ("path".equals(keyStr)) { 739 info.path = valStr; 740 } else if ("size".equals(keyStr)) { 741 info.size = Long.parseLong(valStr); 742 } else { 743 if (DEBUG) { 744 Slog.i(TAG, "Unhandled pax key: " + key); 745 } 746 } 747 748 offset += linelen; 749 } while (offset < contentSize); 750 751 return true; 752 } 753 extractRadix(byte[] data, int offset, int maxChars, int radix)754 private static long extractRadix(byte[] data, int offset, int maxChars, int radix) 755 throws IOException { 756 long value = 0; 757 final int end = offset + maxChars; 758 for (int i = offset; i < end; i++) { 759 final byte b = data[i]; 760 // Numeric fields in tar can terminate with either NUL or SPC 761 if (b == 0 || b == ' ') { 762 break; 763 } 764 if (b < '0' || b > ('0' + radix - 1)) { 765 throw new IOException("Invalid number in header: '" + (char) b 766 + "' for radix " + radix); 767 } 768 value = radix * value + (b - '0'); 769 } 770 return value; 771 } 772 extractString(byte[] data, int offset, int maxChars)773 private static String extractString(byte[] data, int offset, int maxChars) throws IOException { 774 final int end = offset + maxChars; 775 int eos = offset; 776 // tar string fields terminate early with a NUL 777 while (eos < end && data[eos] != 0) { 778 eos++; 779 } 780 return new String(data, offset, eos - offset, "US-ASCII"); 781 } 782 hexLog(byte[] block)783 private static void hexLog(byte[] block) { 784 int offset = 0; 785 int remaining = block.length; 786 StringBuilder buf = new StringBuilder(64); 787 while (remaining > 0) { 788 buf.append(String.format("%04x ", offset)); 789 int numThisLine = (remaining > 16) ? 16 : remaining; 790 for (int i = 0; i < numThisLine; i++) { 791 buf.append(String.format("%02x ", block[offset + i])); 792 } 793 Slog.i("hexdump", buf.toString()); 794 buf.setLength(0); 795 remaining -= numThisLine; 796 offset += numThisLine; 797 } 798 } 799 getMonitor()800 public IBackupManagerMonitor getMonitor() { 801 return mBackupManagerMonitorEventSender.getMonitor(); 802 } 803 getWidgetData()804 public byte[] getWidgetData() { 805 return mWidgetData; 806 } 807 } 808