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.restore; 18 19 import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME; 20 import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME; 21 import static com.android.server.backup.BackupManagerService.DEBUG; 22 import static com.android.server.backup.BackupManagerService.MORE_DEBUG; 23 import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT; 24 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; 25 import static com.android.server.backup.BackupManagerService.TAG; 26 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT; 27 28 import android.app.ApplicationThreadConstants; 29 import android.app.IBackupAgent; 30 import android.app.backup.FullBackup; 31 import android.app.backup.IBackupManagerMonitor; 32 import android.app.backup.IFullBackupRestoreObserver; 33 import android.content.pm.ApplicationInfo; 34 import android.content.pm.PackageInfo; 35 import android.content.pm.PackageManager.NameNotFoundException; 36 import android.content.pm.PackageManagerInternal; 37 import android.content.pm.Signature; 38 import android.os.ParcelFileDescriptor; 39 import android.os.RemoteException; 40 import android.provider.Settings; 41 import android.text.TextUtils; 42 import android.util.Slog; 43 44 import com.android.internal.util.Preconditions; 45 import com.android.server.LocalServices; 46 import com.android.server.backup.BackupAgentTimeoutParameters; 47 import com.android.server.backup.BackupManagerService; 48 import com.android.server.backup.BackupRestoreTask; 49 import com.android.server.backup.FileMetadata; 50 import com.android.server.backup.KeyValueAdbRestoreEngine; 51 import com.android.server.backup.fullbackup.FullBackupObbConnection; 52 import com.android.server.backup.utils.BytesReadListener; 53 import com.android.server.backup.utils.FullBackupRestoreObserverUtils; 54 import com.android.server.backup.utils.RestoreUtils; 55 import com.android.server.backup.utils.TarBackupReader; 56 57 import java.io.FileOutputStream; 58 import java.io.IOException; 59 import java.io.InputStream; 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.HashMap; 63 import java.util.HashSet; 64 import java.util.List; 65 66 /** 67 * Full restore engine, used by both adb restore and transport-based full restore. 68 */ 69 public class FullRestoreEngine extends RestoreEngine { 70 71 private final BackupManagerService mBackupManagerService; 72 // Task in charge of monitoring timeouts 73 private final BackupRestoreTask mMonitorTask; 74 75 private final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 76 77 // Dedicated observer, if any 78 private IFullBackupRestoreObserver mObserver; 79 80 final IBackupManagerMonitor mMonitor; 81 82 // Where we're delivering the file data as we go 83 private IBackupAgent mAgent; 84 85 // Are we permitted to only deliver a specific package's metadata? 86 final PackageInfo mOnlyPackage; 87 88 final boolean mAllowApks; 89 private final boolean mAllowObbs; 90 91 // Which package are we currently handling data for? 92 private String mAgentPackage; 93 94 // Info for working with the target app process 95 private ApplicationInfo mTargetApp; 96 97 // Machinery for restoring OBBs 98 private FullBackupObbConnection mObbConnection = null; 99 100 // possible handling states for a given package in the restore dataset 101 private final HashMap<String, RestorePolicy> mPackagePolicies 102 = new HashMap<>(); 103 104 // installer package names for each encountered app, derived from the manifests 105 private final HashMap<String, String> mPackageInstallers = new HashMap<>(); 106 107 // Signatures for a given package found in its manifest file 108 private final HashMap<String, Signature[]> mManifestSignatures 109 = new HashMap<>(); 110 111 // Packages we've already wiped data on when restoring their first file 112 private final HashSet<String> mClearedPackages = new HashSet<>(); 113 114 // How much data have we moved? 115 private long mBytes; 116 117 // Working buffer 118 final byte[] mBuffer; 119 120 // Pipes for moving data 121 private ParcelFileDescriptor[] mPipes = null; 122 123 // Widget blob to be restored out-of-band 124 private byte[] mWidgetData = null; 125 126 final int mEphemeralOpToken; 127 128 private final BackupAgentTimeoutParameters mAgentTimeoutParameters; 129 130 public FullRestoreEngine(BackupManagerService backupManagerService, 131 BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer, 132 IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks, 133 boolean allowObbs, int ephemeralOpToken) { 134 mBackupManagerService = backupManagerService; 135 mEphemeralOpToken = ephemeralOpToken; 136 mMonitorTask = monitorTask; 137 mObserver = observer; 138 mMonitor = monitor; 139 mOnlyPackage = onlyPackage; 140 mAllowApks = allowApks; 141 mAllowObbs = allowObbs; 142 mBuffer = new byte[32 * 1024]; 143 mBytes = 0; 144 mAgentTimeoutParameters = Preconditions.checkNotNull( 145 backupManagerService.getAgentTimeoutParameters(), 146 "Timeout parameters cannot be null"); 147 } 148 149 public IBackupAgent getAgent() { 150 return mAgent; 151 } 152 153 public byte[] getWidgetData() { 154 return mWidgetData; 155 } 156 157 public boolean restoreOneFile(InputStream instream, boolean mustKillAgent, byte[] buffer, 158 PackageInfo onlyPackage, boolean allowApks, int token, IBackupManagerMonitor monitor) { 159 if (!isRunning()) { 160 Slog.w(TAG, "Restore engine used after halting"); 161 return false; 162 } 163 164 BytesReadListener bytesReadListener = new BytesReadListener() { 165 @Override 166 public void onBytesRead(long bytesRead) { 167 mBytes += bytesRead; 168 } 169 }; 170 171 TarBackupReader tarBackupReader = new TarBackupReader(instream, 172 bytesReadListener, monitor); 173 174 FileMetadata info; 175 try { 176 if (MORE_DEBUG) { 177 Slog.v(TAG, "Reading tar header for restoring file"); 178 } 179 info = tarBackupReader.readTarHeaders(); 180 if (info != null) { 181 if (MORE_DEBUG) { 182 info.dump(); 183 } 184 185 final String pkg = info.packageName; 186 if (!pkg.equals(mAgentPackage)) { 187 // In the single-package case, it's a semantic error to expect 188 // one app's data but see a different app's on the wire 189 if (onlyPackage != null) { 190 if (!pkg.equals(onlyPackage.packageName)) { 191 Slog.w(TAG, "Expected data for " + onlyPackage + " but saw " + pkg); 192 setResult(RestoreEngine.TRANSPORT_FAILURE); 193 setRunning(false); 194 return false; 195 } 196 } 197 198 // okay, change in package; set up our various 199 // bookkeeping if we haven't seen it yet 200 if (!mPackagePolicies.containsKey(pkg)) { 201 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 202 } 203 204 // Clean up the previous agent relationship if necessary, 205 // and let the observer know we're considering a new app. 206 if (mAgent != null) { 207 if (DEBUG) { 208 Slog.d(TAG, "Saw new package; finalizing old one"); 209 } 210 // Now we're really done 211 tearDownPipes(); 212 tearDownAgent(mTargetApp); 213 mTargetApp = null; 214 mAgentPackage = null; 215 } 216 } 217 218 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 219 Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures( 220 info); 221 PackageManagerInternal pmi = LocalServices.getService( 222 PackageManagerInternal.class); 223 RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy( 224 mBackupManagerService.getPackageManager(), allowApks, info, signatures, 225 pmi); 226 mManifestSignatures.put(info.packageName, signatures); 227 mPackagePolicies.put(pkg, restorePolicy); 228 mPackageInstallers.put(pkg, info.installerPackageName); 229 // We've read only the manifest content itself at this point, 230 // so consume the footer before looping around to the next 231 // input file 232 tarBackupReader.skipTarPadding(info.size); 233 mObserver = FullBackupRestoreObserverUtils.sendOnRestorePackage(mObserver, pkg); 234 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 235 // Metadata blobs! 236 tarBackupReader.readMetadata(info); 237 238 // The following only exist because we want to keep refactoring as safe as 239 // possible, without changing too much. 240 // TODO: Refactor, so that there are no funny things like this. 241 // This is read during TarBackupReader.readMetadata(). 242 mWidgetData = tarBackupReader.getWidgetData(); 243 // This can be nulled during TarBackupReader.readMetadata(). 244 monitor = tarBackupReader.getMonitor(); 245 246 tarBackupReader.skipTarPadding(info.size); 247 } else { 248 // Non-manifest, so it's actual file data. Is this a package 249 // we're ignoring? 250 boolean okay = true; 251 RestorePolicy policy = mPackagePolicies.get(pkg); 252 switch (policy) { 253 case IGNORE: 254 okay = false; 255 break; 256 257 case ACCEPT_IF_APK: 258 // If we're in accept-if-apk state, then the first file we 259 // see MUST be the apk. 260 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 261 if (DEBUG) { 262 Slog.d(TAG, "APK file; installing"); 263 } 264 // Try to install the app. 265 String installerPackageName = mPackageInstallers.get(pkg); 266 boolean isSuccessfullyInstalled = RestoreUtils.installApk( 267 instream, mBackupManagerService.getContext(), 268 mDeleteObserver, mManifestSignatures, 269 mPackagePolicies, info, installerPackageName, 270 bytesReadListener); 271 // good to go; promote to ACCEPT 272 mPackagePolicies.put(pkg, isSuccessfullyInstalled 273 ? RestorePolicy.ACCEPT 274 : RestorePolicy.IGNORE); 275 // At this point we've consumed this file entry 276 // ourselves, so just strip the tar footer and 277 // go on to the next file in the input stream 278 tarBackupReader.skipTarPadding(info.size); 279 return true; 280 } else { 281 // File data before (or without) the apk. We can't 282 // handle it coherently in this case so ignore it. 283 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 284 okay = false; 285 } 286 break; 287 288 case ACCEPT: 289 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 290 if (DEBUG) { 291 Slog.d(TAG, "apk present but ACCEPT"); 292 } 293 // we can take the data without the apk, so we 294 // *want* to do so. skip the apk by declaring this 295 // one file not-okay without changing the restore 296 // policy for the package. 297 okay = false; 298 } 299 break; 300 301 default: 302 // Something has gone dreadfully wrong when determining 303 // the restore policy from the manifest. Ignore the 304 // rest of this package's data. 305 Slog.e(TAG, "Invalid policy from manifest"); 306 okay = false; 307 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 308 break; 309 } 310 311 // Is it a *file* we need to drop or is it not a canonical path? 312 if (!isRestorableFile(info) || !isCanonicalFilePath(info.path)) { 313 okay = false; 314 } 315 316 // If the policy is satisfied, go ahead and set up to pipe the 317 // data to the agent. 318 if (MORE_DEBUG && okay && mAgent != null) { 319 Slog.i(TAG, "Reusing existing agent instance"); 320 } 321 if (okay && mAgent == null) { 322 if (MORE_DEBUG) { 323 Slog.d(TAG, "Need to launch agent for " + pkg); 324 } 325 326 try { 327 mTargetApp = 328 mBackupManagerService.getPackageManager().getApplicationInfo( 329 pkg, 0); 330 331 // If we haven't sent any data to this app yet, we probably 332 // need to clear it first. Check that. 333 if (!mClearedPackages.contains(pkg)) { 334 // Apps with their own backup agents are responsible for coherently 335 // managing a full restore. 336 // In some rare cases they can't, especially in case of deferred 337 // restore. In this case check whether this app should be forced to 338 // clear up. 339 // TODO: Fix this properly with manifest parameter. 340 boolean forceClear = shouldForceClearAppDataOnFullRestore( 341 mTargetApp.packageName); 342 if (mTargetApp.backupAgentName == null || forceClear) { 343 if (DEBUG) { 344 Slog.d(TAG, 345 "Clearing app data preparatory to full restore"); 346 } 347 mBackupManagerService.clearApplicationDataSynchronous(pkg, true); 348 } else { 349 if (MORE_DEBUG) { 350 Slog.d(TAG, "backup agent (" 351 + mTargetApp.backupAgentName + ") => no clear"); 352 } 353 } 354 mClearedPackages.add(pkg); 355 } else { 356 if (MORE_DEBUG) { 357 Slog.d(TAG, "We've initialized this app already; no clear " 358 + "required"); 359 } 360 } 361 362 // All set; now set up the IPC and launch the agent 363 setUpPipes(); 364 mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp, 365 ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL); 366 mAgentPackage = pkg; 367 } catch (IOException e) { 368 // fall through to error handling 369 } catch (NameNotFoundException e) { 370 // fall through to error handling 371 } 372 373 if (mAgent == null) { 374 Slog.e(TAG, "Unable to create agent for " + pkg); 375 okay = false; 376 tearDownPipes(); 377 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 378 } 379 } 380 381 // Sanity check: make sure we never give data to the wrong app. This 382 // should never happen but a little paranoia here won't go amiss. 383 if (okay && !pkg.equals(mAgentPackage)) { 384 Slog.e(TAG, "Restoring data for " + pkg 385 + " but agent is for " + mAgentPackage); 386 okay = false; 387 } 388 389 // At this point we have an agent ready to handle the full 390 // restore data as well as a pipe for sending data to 391 // that agent. Tell the agent to start reading from the 392 // pipe. 393 if (okay) { 394 boolean agentSuccess = true; 395 long toCopy = info.size; 396 final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE); 397 final long timeout = isSharedStorage ? 398 mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() : 399 mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(); 400 try { 401 mBackupManagerService.prepareOperationTimeout(token, 402 timeout, 403 mMonitorTask, 404 OP_TYPE_RESTORE_WAIT); 405 406 if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) { 407 if (DEBUG) { 408 Slog.d(TAG, "Restoring OBB file for " + pkg 409 + " : " + info.path); 410 } 411 mObbConnection.restoreObbFile(pkg, mPipes[0], 412 info.size, info.type, info.path, info.mode, 413 info.mtime, token, 414 mBackupManagerService.getBackupManagerBinder()); 415 } else if (FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)) { 416 // This is only possible during adb restore. 417 // TODO: Refactor to clearly separate the flows. 418 if (DEBUG) { 419 Slog.d(TAG, "Restoring key-value file for " + pkg 420 + " : " + info.path); 421 } 422 KeyValueAdbRestoreEngine restoreEngine = 423 new KeyValueAdbRestoreEngine( 424 mBackupManagerService, 425 mBackupManagerService.getDataDir(), info, mPipes[0], 426 mAgent, token); 427 new Thread(restoreEngine, "restore-key-value-runner").start(); 428 } else { 429 if (MORE_DEBUG) { 430 Slog.d(TAG, "Invoking agent to restore file " + info.path); 431 } 432 // fire up the app's agent listening on the socket. If 433 // the agent is running in the system process we can't 434 // just invoke it asynchronously, so we provide a thread 435 // for it here. 436 if (mTargetApp.processName.equals("system")) { 437 Slog.d(TAG, "system process agent - spinning a thread"); 438 RestoreFileRunnable runner = new RestoreFileRunnable( 439 mBackupManagerService, mAgent, info, mPipes[0], token); 440 new Thread(runner, "restore-sys-runner").start(); 441 } else { 442 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 443 info.domain, info.path, info.mode, info.mtime, 444 token, mBackupManagerService.getBackupManagerBinder()); 445 } 446 } 447 } catch (IOException e) { 448 // couldn't dup the socket for a process-local restore 449 Slog.d(TAG, "Couldn't establish restore"); 450 agentSuccess = false; 451 okay = false; 452 } catch (RemoteException e) { 453 // whoops, remote entity went away. We'll eat the content 454 // ourselves, then, and not copy it over. 455 Slog.e(TAG, "Agent crashed during full restore"); 456 agentSuccess = false; 457 okay = false; 458 } 459 460 // Copy over the data if the agent is still good 461 if (okay) { 462 if (MORE_DEBUG) { 463 Slog.v(TAG, " copying to restore agent: " + toCopy + " bytes"); 464 } 465 boolean pipeOkay = true; 466 FileOutputStream pipe = new FileOutputStream( 467 mPipes[1].getFileDescriptor()); 468 while (toCopy > 0) { 469 int toRead = (toCopy > buffer.length) 470 ? buffer.length : (int) toCopy; 471 int nRead = instream.read(buffer, 0, toRead); 472 if (nRead >= 0) { 473 mBytes += nRead; 474 } 475 if (nRead <= 0) { 476 break; 477 } 478 toCopy -= nRead; 479 480 // send it to the output pipe as long as things 481 // are still good 482 if (pipeOkay) { 483 try { 484 pipe.write(buffer, 0, nRead); 485 } catch (IOException e) { 486 Slog.e(TAG, "Failed to write to restore pipe: " 487 + e.getMessage()); 488 pipeOkay = false; 489 } 490 } 491 } 492 493 // done sending that file! Now we just need to consume 494 // the delta from info.size to the end of block. 495 tarBackupReader.skipTarPadding(info.size); 496 497 // and now that we've sent it all, wait for the remote 498 // side to acknowledge receipt 499 agentSuccess = mBackupManagerService.waitUntilOperationComplete(token); 500 } 501 502 // okay, if the remote end failed at any point, deal with 503 // it by ignoring the rest of the restore on it 504 if (!agentSuccess) { 505 Slog.w(TAG, "Agent failure restoring " + pkg + "; ending restore"); 506 mBackupManagerService.getBackupHandler().removeMessages( 507 MSG_RESTORE_OPERATION_TIMEOUT); 508 tearDownPipes(); 509 tearDownAgent(mTargetApp); 510 mAgent = null; 511 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 512 513 // If this was a single-package restore, we halt immediately 514 // with an agent error under these circumstances 515 if (onlyPackage != null) { 516 setResult(RestoreEngine.TARGET_FAILURE); 517 setRunning(false); 518 return false; 519 } 520 } 521 } 522 523 // Problems setting up the agent communication, an explicitly 524 // dropped file, or an already-ignored package: skip to the 525 // next stream entry by reading and discarding this file. 526 if (!okay) { 527 if (MORE_DEBUG) { 528 Slog.d(TAG, "[discarding file content]"); 529 } 530 long bytesToConsume = (info.size + 511) & ~511; 531 while (bytesToConsume > 0) { 532 int toRead = (bytesToConsume > buffer.length) 533 ? buffer.length : (int) bytesToConsume; 534 long nRead = instream.read(buffer, 0, toRead); 535 if (nRead >= 0) { 536 mBytes += nRead; 537 } 538 if (nRead <= 0) { 539 break; 540 } 541 bytesToConsume -= nRead; 542 } 543 } 544 } 545 } 546 } catch (IOException e) { 547 if (DEBUG) { 548 Slog.w(TAG, "io exception on restore socket read: " + e.getMessage()); 549 } 550 setResult(RestoreEngine.TRANSPORT_FAILURE); 551 info = null; 552 } 553 554 // If we got here we're either running smoothly or we've finished 555 if (info == null) { 556 if (MORE_DEBUG) { 557 Slog.i(TAG, "No [more] data for this package; tearing down"); 558 } 559 tearDownPipes(); 560 setRunning(false); 561 if (mustKillAgent) { 562 tearDownAgent(mTargetApp); 563 } 564 } 565 return (info != null); 566 } 567 568 private void setUpPipes() throws IOException { 569 mPipes = ParcelFileDescriptor.createPipe(); 570 } 571 572 private void tearDownPipes() { 573 // Teardown might arise from the inline restore processing or from the asynchronous 574 // timeout mechanism, and these might race. Make sure we don't try to close and 575 // null out the pipes twice. 576 synchronized (this) { 577 if (mPipes != null) { 578 try { 579 mPipes[0].close(); 580 mPipes[0] = null; 581 mPipes[1].close(); 582 mPipes[1] = null; 583 } catch (IOException e) { 584 Slog.w(TAG, "Couldn't close agent pipes", e); 585 } 586 mPipes = null; 587 } 588 } 589 } 590 591 private void tearDownAgent(ApplicationInfo app) { 592 if (mAgent != null) { 593 mBackupManagerService.tearDownAgentAndKill(app); 594 mAgent = null; 595 } 596 } 597 598 void handleTimeout() { 599 tearDownPipes(); 600 setResult(RestoreEngine.TARGET_FAILURE); 601 setRunning(false); 602 } 603 604 private static boolean isRestorableFile(FileMetadata info) { 605 if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { 606 if (MORE_DEBUG) { 607 Slog.i(TAG, "Dropping cache file path " + info.path); 608 } 609 return false; 610 } 611 612 if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) { 613 // It's possible this is "no-backup" dir contents in an archive stream 614 // produced on a device running a version of the OS that predates that 615 // API. Respect the no-backup intention and don't let the data get to 616 // the app. 617 if (info.path.startsWith("no_backup/")) { 618 if (MORE_DEBUG) { 619 Slog.i(TAG, "Dropping no_backup file path " + info.path); 620 } 621 return false; 622 } 623 } 624 625 // Otherwise we think this file is good to go 626 return true; 627 } 628 629 private static boolean isCanonicalFilePath(String path) { 630 if (path.contains("..") || path.contains("//")) { 631 if (MORE_DEBUG) { 632 Slog.w(TAG, "Dropping invalid path " + path); 633 } 634 return false; 635 } 636 637 return true; 638 } 639 640 /** 641 * Returns whether the package is in the list of the packages for which clear app data should 642 * be called despite the fact that they have backup agent. 643 * 644 * <p>The list is read from {@link Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}. 645 */ 646 private boolean shouldForceClearAppDataOnFullRestore(String packageName) { 647 String packageListString = Settings.Secure.getString( 648 mBackupManagerService.getContext().getContentResolver(), 649 Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE); 650 if (TextUtils.isEmpty(packageListString)) { 651 return false; 652 } 653 654 List<String> packages = Arrays.asList(packageListString.split(";")); 655 return packages.contains(packageName); 656 } 657 658 void sendOnRestorePackage(String name) { 659 if (mObserver != null) { 660 try { 661 // TODO: use a more user-friendly name string 662 mObserver.onRestorePackage(name); 663 } catch (RemoteException e) { 664 Slog.w(TAG, "full restore observer went away: restorePackage"); 665 mObserver = null; 666 } 667 } 668 } 669 } 670