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