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 17package com.android.server.backup.fullbackup; 18 19import static com.android.server.backup.RefactoredBackupManagerService.DEBUG; 20import static com.android.server.backup.RefactoredBackupManagerService.DEBUG_SCHEDULING; 21import static com.android.server.backup.RefactoredBackupManagerService.MORE_DEBUG; 22import static com.android.server.backup.RefactoredBackupManagerService.OP_PENDING; 23import static com.android.server.backup.RefactoredBackupManagerService.OP_TYPE_BACKUP; 24import static com.android.server.backup.RefactoredBackupManagerService.OP_TYPE_BACKUP_WAIT; 25import static com.android.server.backup.RefactoredBackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL; 26 27import android.app.IBackupAgent; 28import android.app.backup.BackupManager; 29import android.app.backup.BackupManagerMonitor; 30import android.app.backup.BackupProgress; 31import android.app.backup.BackupTransport; 32import android.app.backup.IBackupManagerMonitor; 33import android.app.backup.IBackupObserver; 34import android.app.backup.IFullBackupRestoreObserver; 35import android.content.pm.PackageInfo; 36import android.content.pm.PackageManager; 37import android.content.pm.PackageManager.NameNotFoundException; 38import android.os.ParcelFileDescriptor; 39import android.os.RemoteException; 40import android.util.EventLog; 41import android.util.Log; 42import android.util.Slog; 43 44import com.android.internal.backup.IBackupTransport; 45import com.android.server.EventLogTags; 46import com.android.server.backup.BackupRestoreTask; 47import com.android.server.backup.FullBackupJob; 48import com.android.server.backup.RefactoredBackupManagerService; 49import com.android.server.backup.internal.Operation; 50import com.android.server.backup.utils.AppBackupUtils; 51import com.android.server.backup.utils.BackupManagerMonitorUtils; 52import com.android.server.backup.utils.BackupObserverUtils; 53 54import java.io.FileInputStream; 55import java.io.FileOutputStream; 56import java.io.IOException; 57import java.util.ArrayList; 58import java.util.concurrent.CountDownLatch; 59import java.util.concurrent.TimeUnit; 60import java.util.concurrent.atomic.AtomicLong; 61 62/** 63 * Full backup task extension used for transport-oriented operation. 64 * 65 * Flow: 66 * For each requested package: 67 * - Spin off a new SinglePackageBackupRunner (mBackupRunner) for the current package. 68 * - Wait until preflight is complete. (mBackupRunner.getPreflightResultBlocking()) 69 * - If preflight data size is within limit, start reading data from agent pipe and writing 70 * to transport pipe. While there is data to send, call transport.sendBackupData(int) to 71 * tell the transport how many bytes to expect on its pipe. 72 * - After sending all data, call transport.finishBackup() if things went well. And 73 * transport.cancelFullBackup() otherwise. 74 * 75 * Interactions with mCurrentOperations: 76 * - An entry for this object is added to mCurrentOperations for the entire lifetime of this 77 * object. Used to cancel the operation. 78 * - SinglePackageBackupRunner and SinglePackageBackupPreflight will put ephemeral entries 79 * to get timeouts or operation complete callbacks. 80 * 81 * Handling cancels: 82 * - The contract we provide is that the task won't interact with the transport after 83 * handleCancel() is done executing. 84 * - This task blocks at 3 points: 1. Preflight result check 2. Reading on agent side pipe 85 * and 3. Get backup result from mBackupRunner. 86 * - Bubbling up handleCancel to mBackupRunner handles all 3: 1. Calls handleCancel on the 87 * preflight operation which counts down on the preflight latch. 2. Tears down the agent, 88 * so read() returns -1. 3. Notifies mCurrentOpLock which unblocks 89 * mBackupRunner.getBackupResultBlocking(). 90 */ 91public class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask { 92 private static final String TAG = "PFTBT"; 93 94 private RefactoredBackupManagerService backupManagerService; 95 private final Object mCancelLock = new Object(); 96 97 ArrayList<PackageInfo> mPackages; 98 PackageInfo mCurrentPackage; 99 boolean mUpdateSchedule; 100 CountDownLatch mLatch; 101 FullBackupJob mJob; // if a scheduled job needs to be finished afterwards 102 IBackupObserver mBackupObserver; 103 IBackupManagerMonitor mMonitor; 104 boolean mUserInitiated; 105 private volatile IBackupTransport mTransport; 106 SinglePackageBackupRunner mBackupRunner; 107 private final int mBackupRunnerOpToken; 108 109 // This is true when a backup operation for some package is in progress. 110 private volatile boolean mIsDoingBackup; 111 private volatile boolean mCancelAll; 112 private final int mCurrentOpToken; 113 114 public PerformFullTransportBackupTask(RefactoredBackupManagerService backupManagerService, 115 IFullBackupRestoreObserver observer, 116 String[] whichPackages, boolean updateSchedule, 117 FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver, 118 IBackupManagerMonitor monitor, boolean userInitiated) { 119 super(observer); 120 this.backupManagerService = backupManagerService; 121 mUpdateSchedule = updateSchedule; 122 mLatch = latch; 123 mJob = runningJob; 124 mPackages = new ArrayList<>(whichPackages.length); 125 mBackupObserver = backupObserver; 126 mMonitor = monitor; 127 mUserInitiated = userInitiated; 128 mCurrentOpToken = backupManagerService.generateRandomIntegerToken(); 129 mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken(); 130 131 if (backupManagerService.isBackupOperationInProgress()) { 132 if (DEBUG) { 133 Slog.d(TAG, "Skipping full backup. A backup is already in progress."); 134 } 135 mCancelAll = true; 136 return; 137 } 138 139 registerTask(); 140 141 for (String pkg : whichPackages) { 142 try { 143 PackageInfo info = backupManagerService.getPackageManager().getPackageInfo(pkg, 144 PackageManager.GET_SIGNATURES); 145 mCurrentPackage = info; 146 if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo)) { 147 // Cull any packages that have indicated that backups are not permitted, 148 // that run as system-domain uids but do not define their own backup agents, 149 // as well as any explicit mention of the 'special' shared-storage agent 150 // package (we handle that one at the end). 151 if (MORE_DEBUG) { 152 Slog.d(TAG, "Ignoring ineligible package " + pkg); 153 } 154 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 155 BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE, 156 mCurrentPackage, 157 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 158 null); 159 BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, pkg, 160 BackupManager.ERROR_BACKUP_NOT_ALLOWED); 161 continue; 162 } else if (!AppBackupUtils.appGetsFullBackup(info)) { 163 // Cull any packages that are found in the queue but now aren't supposed 164 // to get full-data backup operations. 165 if (MORE_DEBUG) { 166 Slog.d(TAG, "Ignoring full-data backup of key/value participant " 167 + pkg); 168 } 169 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 170 BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT, 171 mCurrentPackage, 172 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 173 null); 174 BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, pkg, 175 BackupManager.ERROR_BACKUP_NOT_ALLOWED); 176 continue; 177 } else if (AppBackupUtils.appIsStopped(info.applicationInfo)) { 178 // Cull any packages in the 'stopped' state: they've either just been 179 // installed or have explicitly been force-stopped by the user. In both 180 // cases we do not want to launch them for backup. 181 if (MORE_DEBUG) { 182 Slog.d(TAG, "Ignoring stopped package " + pkg); 183 } 184 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 185 BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED, 186 mCurrentPackage, 187 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 188 null); 189 BackupObserverUtils.sendBackupOnPackageResult(mBackupObserver, pkg, 190 BackupManager.ERROR_BACKUP_NOT_ALLOWED); 191 continue; 192 } 193 mPackages.add(info); 194 } catch (NameNotFoundException e) { 195 Slog.i(TAG, "Requested package " + pkg + " not found; ignoring"); 196 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 197 BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND, 198 mCurrentPackage, 199 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 200 null); 201 } 202 } 203 } 204 205 private void registerTask() { 206 synchronized (backupManagerService.getCurrentOpLock()) { 207 Slog.d(TAG, "backupmanager pftbt token=" + Integer.toHexString(mCurrentOpToken)); 208 backupManagerService.getCurrentOperations().put( 209 mCurrentOpToken, 210 new Operation(OP_PENDING, this, OP_TYPE_BACKUP)); 211 } 212 } 213 214 public void unregisterTask() { 215 backupManagerService.removeOperation(mCurrentOpToken); 216 } 217 218 @Override 219 public void execute() { 220 // Nothing to do. 221 } 222 223 @Override 224 public void handleCancel(boolean cancelAll) { 225 synchronized (mCancelLock) { 226 // We only support 'cancelAll = true' case for this task. Cancelling of a single package 227 228 // due to timeout is handled by SinglePackageBackupRunner and 229 // SinglePackageBackupPreflight. 230 231 if (!cancelAll) { 232 Slog.wtf(TAG, "Expected cancelAll to be true."); 233 } 234 235 if (mCancelAll) { 236 Slog.d(TAG, "Ignoring duplicate cancel call."); 237 return; 238 } 239 240 mCancelAll = true; 241 if (mIsDoingBackup) { 242 backupManagerService.handleCancel(mBackupRunnerOpToken, cancelAll); 243 try { 244 mTransport.cancelFullBackup(); 245 } catch (RemoteException e) { 246 Slog.w(TAG, "Error calling cancelFullBackup() on transport: " + e); 247 // Can't do much. 248 } 249 } 250 } 251 } 252 253 @Override 254 public void operationComplete(long result) { 255 // Nothing to do. 256 } 257 258 @Override 259 public void run() { 260 261 // data from the app, passed to us for bridging to the transport 262 ParcelFileDescriptor[] enginePipes = null; 263 264 // Pipe through which we write data to the transport 265 ParcelFileDescriptor[] transportPipes = null; 266 267 long backoff = 0; 268 int backupRunStatus = BackupManager.SUCCESS; 269 270 try { 271 if (!backupManagerService.isEnabled() || !backupManagerService.isProvisioned()) { 272 // Backups are globally disabled, so don't proceed. 273 if (DEBUG) { 274 Slog.i(TAG, "full backup requested but enabled=" + backupManagerService 275 .isEnabled() 276 + " provisioned=" + backupManagerService.isProvisioned() 277 + "; ignoring"); 278 } 279 int monitoringEvent; 280 if (backupManagerService.isProvisioned()) { 281 monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED; 282 } else { 283 monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED; 284 } 285 mMonitor = BackupManagerMonitorUtils 286 .monitorEvent(mMonitor, monitoringEvent, null, 287 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 288 null); 289 mUpdateSchedule = false; 290 backupRunStatus = BackupManager.ERROR_BACKUP_NOT_ALLOWED; 291 return; 292 } 293 294 mTransport = backupManagerService.getTransportManager().getCurrentTransportBinder(); 295 if (mTransport == null) { 296 Slog.w(TAG, "Transport not present; full data backup not performed"); 297 backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED; 298 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 299 BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT, 300 mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, 301 null); 302 return; 303 } 304 305 // Set up to send data to the transport 306 final int N = mPackages.size(); 307 final byte[] buffer = new byte[8192]; 308 for (int i = 0; i < N; i++) { 309 PackageInfo currentPackage = mPackages.get(i); 310 String packageName = currentPackage.packageName; 311 if (DEBUG) { 312 Slog.i(TAG, "Initiating full-data transport backup of " + packageName 313 + " token: " + mCurrentOpToken); 314 } 315 EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, packageName); 316 317 transportPipes = ParcelFileDescriptor.createPipe(); 318 319 // Tell the transport the data's coming 320 int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0; 321 int backupPackageStatus; 322 long quota = Long.MAX_VALUE; 323 synchronized (mCancelLock) { 324 if (mCancelAll) { 325 break; 326 } 327 backupPackageStatus = mTransport.performFullBackup(currentPackage, 328 transportPipes[0], flags); 329 330 if (backupPackageStatus == BackupTransport.TRANSPORT_OK) { 331 quota = mTransport.getBackupQuota(currentPackage.packageName, 332 true /* isFullBackup */); 333 // Now set up the backup engine / data source end of things 334 enginePipes = ParcelFileDescriptor.createPipe(); 335 mBackupRunner = 336 new SinglePackageBackupRunner(enginePipes[1], currentPackage, 337 mTransport, quota, mBackupRunnerOpToken); 338 // The runner dup'd the pipe half, so we close it here 339 enginePipes[1].close(); 340 enginePipes[1] = null; 341 342 mIsDoingBackup = true; 343 } 344 } 345 if (backupPackageStatus == BackupTransport.TRANSPORT_OK) { 346 347 // The transport has its own copy of the read end of the pipe, 348 // so close ours now 349 transportPipes[0].close(); 350 transportPipes[0] = null; 351 352 // Spin off the runner to fetch the app's data and pipe it 353 // into the engine pipes 354 (new Thread(mBackupRunner, "package-backup-bridge")).start(); 355 356 // Read data off the engine pipe and pass it to the transport 357 // pipe until we hit EOD on the input stream. We do not take 358 // close() responsibility for these FDs into these stream wrappers. 359 FileInputStream in = new FileInputStream( 360 enginePipes[0].getFileDescriptor()); 361 FileOutputStream out = new FileOutputStream( 362 transportPipes[1].getFileDescriptor()); 363 long totalRead = 0; 364 final long preflightResult = mBackupRunner.getPreflightResultBlocking(); 365 // Preflight result is negative if some error happened on preflight. 366 if (preflightResult < 0) { 367 if (MORE_DEBUG) { 368 Slog.d(TAG, "Backup error after preflight of package " 369 + packageName + ": " + preflightResult 370 + ", not running backup."); 371 } 372 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 373 BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT, 374 mCurrentPackage, 375 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 376 BackupManagerMonitorUtils.putMonitoringExtra(null, 377 BackupManagerMonitor.EXTRA_LOG_PREFLIGHT_ERROR, 378 preflightResult)); 379 backupPackageStatus = (int) preflightResult; 380 } else { 381 int nRead = 0; 382 do { 383 nRead = in.read(buffer); 384 if (MORE_DEBUG) { 385 Slog.v(TAG, "in.read(buffer) from app: " + nRead); 386 } 387 if (nRead > 0) { 388 out.write(buffer, 0, nRead); 389 synchronized (mCancelLock) { 390 if (!mCancelAll) { 391 backupPackageStatus = mTransport.sendBackupData(nRead); 392 } 393 } 394 totalRead += nRead; 395 if (mBackupObserver != null && preflightResult > 0) { 396 BackupObserverUtils 397 .sendBackupOnUpdate(mBackupObserver, packageName, 398 new BackupProgress(preflightResult, totalRead)); 399 } 400 } 401 } while (nRead > 0 402 && backupPackageStatus == BackupTransport.TRANSPORT_OK); 403 // Despite preflight succeeded, package still can hit quota on flight. 404 if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { 405 Slog.w(TAG, "Package hit quota limit in-flight " + packageName 406 + ": " + totalRead + " of " + quota); 407 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 408 BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT, 409 mCurrentPackage, 410 BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, 411 null); 412 mBackupRunner.sendQuotaExceeded(totalRead, quota); 413 } 414 } 415 416 final int backupRunnerResult = mBackupRunner.getBackupResultBlocking(); 417 418 synchronized (mCancelLock) { 419 mIsDoingBackup = false; 420 // If mCancelCurrent is true, we have already called cancelFullBackup(). 421 if (!mCancelAll) { 422 if (backupRunnerResult == BackupTransport.TRANSPORT_OK) { 423 // If we were otherwise in a good state, now interpret the final 424 // result based on what finishBackup() returns. If we're in a 425 // failure case already, preserve that result and ignore whatever 426 // finishBackup() reports. 427 final int finishResult = mTransport.finishBackup(); 428 if (backupPackageStatus == BackupTransport.TRANSPORT_OK) { 429 backupPackageStatus = finishResult; 430 } 431 } else { 432 mTransport.cancelFullBackup(); 433 } 434 } 435 } 436 437 // A transport-originated error here means that we've hit an error that the 438 // runner doesn't know about, so it's still moving data but we're pulling the 439 // rug out from under it. Don't ask for its result: we already know better 440 // and we'll hang if we block waiting for it, since it relies on us to 441 // read back the data it's writing into the engine. Just proceed with 442 // a graceful failure. The runner/engine mechanism will tear itself 443 // down cleanly when we close the pipes from this end. Transport-level 444 // errors take precedence over agent/app-specific errors for purposes of 445 // determining our course of action. 446 if (backupPackageStatus == BackupTransport.TRANSPORT_OK) { 447 // We still could fail in backup runner thread. 448 if (backupRunnerResult != BackupTransport.TRANSPORT_OK) { 449 // If there was an error in runner thread and 450 // not TRANSPORT_ERROR here, overwrite it. 451 backupPackageStatus = backupRunnerResult; 452 } 453 } else { 454 if (MORE_DEBUG) { 455 Slog.i(TAG, "Transport-level failure; cancelling agent work"); 456 } 457 } 458 459 if (MORE_DEBUG) { 460 Slog.i(TAG, "Done delivering backup data: result=" 461 + backupPackageStatus); 462 } 463 464 if (backupPackageStatus != BackupTransport.TRANSPORT_OK) { 465 Slog.e(TAG, "Error " + backupPackageStatus + " backing up " 466 + packageName); 467 } 468 469 // Also ask the transport how long it wants us to wait before 470 // moving on to the next package, if any. 471 backoff = mTransport.requestFullBackupTime(); 472 if (DEBUG_SCHEDULING) { 473 Slog.i(TAG, "Transport suggested backoff=" + backoff); 474 } 475 476 } 477 478 // Roll this package to the end of the backup queue if we're 479 // in a queue-driven mode (regardless of success/failure) 480 if (mUpdateSchedule) { 481 backupManagerService.enqueueFullBackup(packageName, System.currentTimeMillis()); 482 } 483 484 if (backupPackageStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 485 BackupObserverUtils 486 .sendBackupOnPackageResult(mBackupObserver, packageName, 487 BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED); 488 if (DEBUG) { 489 Slog.i(TAG, "Transport rejected backup of " + packageName 490 + ", skipping"); 491 } 492 EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName, 493 "transport rejected"); 494 // Do nothing, clean up, and continue looping. 495 } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { 496 BackupObserverUtils 497 .sendBackupOnPackageResult(mBackupObserver, packageName, 498 BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED); 499 if (DEBUG) { 500 Slog.i(TAG, "Transport quota exceeded for package: " + packageName); 501 EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED, 502 packageName); 503 } 504 // Do nothing, clean up, and continue looping. 505 } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) { 506 BackupObserverUtils 507 .sendBackupOnPackageResult(mBackupObserver, packageName, 508 BackupManager.ERROR_AGENT_FAILURE); 509 Slog.w(TAG, "Application failure for package: " + packageName); 510 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName); 511 backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); 512 // Do nothing, clean up, and continue looping. 513 } else if (backupPackageStatus == BackupManager.ERROR_BACKUP_CANCELLED) { 514 BackupObserverUtils 515 .sendBackupOnPackageResult(mBackupObserver, packageName, 516 BackupManager.ERROR_BACKUP_CANCELLED); 517 Slog.w(TAG, "Backup cancelled. package=" + packageName + 518 ", cancelAll=" + mCancelAll); 519 EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName); 520 backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); 521 // Do nothing, clean up, and continue looping. 522 } else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) { 523 BackupObserverUtils 524 .sendBackupOnPackageResult(mBackupObserver, packageName, 525 BackupManager.ERROR_TRANSPORT_ABORTED); 526 Slog.w(TAG, "Transport failed; aborting backup: " + backupPackageStatus); 527 EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE); 528 // Abort entire backup pass. 529 backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED; 530 return; 531 } else { 532 // Success! 533 BackupObserverUtils 534 .sendBackupOnPackageResult(mBackupObserver, packageName, 535 BackupManager.SUCCESS); 536 EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, packageName); 537 backupManagerService.logBackupComplete(packageName); 538 } 539 cleanUpPipes(transportPipes); 540 cleanUpPipes(enginePipes); 541 if (currentPackage.applicationInfo != null) { 542 Slog.i(TAG, "Unbinding agent in " + packageName); 543 backupManagerService.addBackupTrace("unbinding " + packageName); 544 try { 545 backupManagerService.getActivityManager().unbindBackupAgent( 546 currentPackage.applicationInfo); 547 } catch (RemoteException e) { /* can't happen; activity manager is local */ } 548 } 549 } 550 } catch (Exception e) { 551 backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED; 552 Slog.w(TAG, "Exception trying full transport backup", e); 553 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 554 BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP, 555 mCurrentPackage, 556 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 557 BackupManagerMonitorUtils.putMonitoringExtra(null, 558 BackupManagerMonitor.EXTRA_LOG_EXCEPTION_FULL_BACKUP, 559 Log.getStackTraceString(e))); 560 561 } finally { 562 563 if (mCancelAll) { 564 backupRunStatus = BackupManager.ERROR_BACKUP_CANCELLED; 565 } 566 567 if (DEBUG) { 568 Slog.i(TAG, "Full backup completed with status: " + backupRunStatus); 569 } 570 BackupObserverUtils.sendBackupFinished(mBackupObserver, backupRunStatus); 571 572 cleanUpPipes(transportPipes); 573 cleanUpPipes(enginePipes); 574 575 unregisterTask(); 576 577 if (mJob != null) { 578 mJob.finishBackupPass(); 579 } 580 581 synchronized (backupManagerService.getQueueLock()) { 582 backupManagerService.setRunningFullBackupTask(null); 583 } 584 585 mLatch.countDown(); 586 587 // Now that we're actually done with schedule-driven work, reschedule 588 // the next pass based on the new queue state. 589 if (mUpdateSchedule) { 590 backupManagerService.scheduleNextFullBackupJob(backoff); 591 } 592 593 Slog.i(TAG, "Full data backup pass finished."); 594 backupManagerService.getWakelock().release(); 595 } 596 } 597 598 void cleanUpPipes(ParcelFileDescriptor[] pipes) { 599 if (pipes != null) { 600 if (pipes[0] != null) { 601 ParcelFileDescriptor fd = pipes[0]; 602 pipes[0] = null; 603 try { 604 fd.close(); 605 } catch (IOException e) { 606 Slog.w(TAG, "Unable to close pipe!"); 607 } 608 } 609 if (pipes[1] != null) { 610 ParcelFileDescriptor fd = pipes[1]; 611 pipes[1] = null; 612 try { 613 fd.close(); 614 } catch (IOException e) { 615 Slog.w(TAG, "Unable to close pipe!"); 616 } 617 } 618 } 619 } 620 621 // Run the backup and pipe it back to the given socket -- expects to run on 622 // a standalone thread. The runner owns this half of the pipe, and closes 623 // it to indicate EOD to the other end. 624 class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight { 625 final AtomicLong mResult = new AtomicLong(BackupTransport.AGENT_ERROR); 626 final CountDownLatch mLatch = new CountDownLatch(1); 627 final IBackupTransport mTransport; 628 final long mQuota; 629 private final int mCurrentOpToken; 630 631 SinglePackageBackupPreflight(IBackupTransport transport, long quota, int currentOpToken) { 632 mTransport = transport; 633 mQuota = quota; 634 mCurrentOpToken = currentOpToken; 635 } 636 637 @Override 638 public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) { 639 int result; 640 try { 641 backupManagerService.prepareOperationTimeout( 642 mCurrentOpToken, TIMEOUT_FULL_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT); 643 backupManagerService.addBackupTrace("preflighting"); 644 if (MORE_DEBUG) { 645 Slog.d(TAG, "Preflighting full payload of " + pkg.packageName); 646 } 647 agent.doMeasureFullBackup(mQuota, mCurrentOpToken, 648 backupManagerService.getBackupManagerBinder()); 649 650 // Now wait to get our result back. If this backstop timeout is reached without 651 // the latch being thrown, flow will continue as though a result or "normal" 652 // timeout had been produced. In case of a real backstop timeout, mResult 653 // will still contain the value it was constructed with, AGENT_ERROR, which 654 // intentionaly falls into the "just report failure" code. 655 mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 656 657 long totalSize = mResult.get(); 658 // If preflight timed out, mResult will contain error code as int. 659 if (totalSize < 0) { 660 return (int) totalSize; 661 } 662 if (MORE_DEBUG) { 663 Slog.v(TAG, "Got preflight response; size=" + totalSize); 664 } 665 666 result = mTransport.checkFullBackupSize(totalSize); 667 if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { 668 if (MORE_DEBUG) { 669 Slog.d(TAG, "Package hit quota limit on preflight " + 670 pkg.packageName + ": " + totalSize + " of " + mQuota); 671 } 672 agent.doQuotaExceeded(totalSize, mQuota); 673 } 674 } catch (Exception e) { 675 Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage()); 676 result = BackupTransport.AGENT_ERROR; 677 } 678 return result; 679 } 680 681 @Override 682 public void execute() { 683 // Unused. 684 } 685 686 @Override 687 public void operationComplete(long result) { 688 // got the callback, and our preflightFullBackup() method is waiting for the result 689 if (MORE_DEBUG) { 690 Slog.i(TAG, "Preflight op complete, result=" + result); 691 } 692 mResult.set(result); 693 mLatch.countDown(); 694 backupManagerService.removeOperation(mCurrentOpToken); 695 } 696 697 @Override 698 public void handleCancel(boolean cancelAll) { 699 if (MORE_DEBUG) { 700 Slog.i(TAG, "Preflight cancelled; failing"); 701 } 702 mResult.set(BackupTransport.AGENT_ERROR); 703 mLatch.countDown(); 704 backupManagerService.removeOperation(mCurrentOpToken); 705 } 706 707 @Override 708 public long getExpectedSizeOrErrorCode() { 709 try { 710 mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 711 return mResult.get(); 712 } catch (InterruptedException e) { 713 return BackupTransport.NO_MORE_DATA; 714 } 715 } 716 } 717 718 class SinglePackageBackupRunner implements Runnable, BackupRestoreTask { 719 final ParcelFileDescriptor mOutput; 720 final PackageInfo mTarget; 721 final SinglePackageBackupPreflight mPreflight; 722 final CountDownLatch mPreflightLatch; 723 final CountDownLatch mBackupLatch; 724 private final int mCurrentOpToken; 725 private final int mEphemeralToken; 726 private FullBackupEngine mEngine; 727 private volatile int mPreflightResult; 728 private volatile int mBackupResult; 729 private final long mQuota; 730 private volatile boolean mIsCancelled; 731 732 SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target, 733 IBackupTransport transport, long quota, int currentOpToken) throws IOException { 734 mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor()); 735 mTarget = target; 736 mCurrentOpToken = currentOpToken; 737 mEphemeralToken = backupManagerService.generateRandomIntegerToken(); 738 mPreflight = new SinglePackageBackupPreflight(transport, quota, mEphemeralToken); 739 mPreflightLatch = new CountDownLatch(1); 740 mBackupLatch = new CountDownLatch(1); 741 mPreflightResult = BackupTransport.AGENT_ERROR; 742 mBackupResult = BackupTransport.AGENT_ERROR; 743 mQuota = quota; 744 registerTask(); 745 } 746 747 void registerTask() { 748 synchronized (backupManagerService.getCurrentOpLock()) { 749 backupManagerService.getCurrentOperations().put( 750 mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP_WAIT)); 751 } 752 } 753 754 void unregisterTask() { 755 synchronized (backupManagerService.getCurrentOpLock()) { 756 backupManagerService.getCurrentOperations().remove(mCurrentOpToken); 757 } 758 } 759 760 @Override 761 public void run() { 762 FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor()); 763 mEngine = new FullBackupEngine(backupManagerService, out, mPreflight, mTarget, false, 764 this, mQuota, mCurrentOpToken); 765 try { 766 try { 767 if (!mIsCancelled) { 768 mPreflightResult = mEngine.preflightCheck(); 769 } 770 } finally { 771 mPreflightLatch.countDown(); 772 } 773 // If there is no error on preflight, continue backup. 774 if (mPreflightResult == BackupTransport.TRANSPORT_OK) { 775 if (!mIsCancelled) { 776 mBackupResult = mEngine.backupOnePackage(); 777 } 778 } 779 } catch (Exception e) { 780 Slog.e(TAG, "Exception during full package backup of " + mTarget.packageName); 781 } finally { 782 unregisterTask(); 783 mBackupLatch.countDown(); 784 try { 785 mOutput.close(); 786 } catch (IOException e) { 787 Slog.w(TAG, "Error closing transport pipe in runner"); 788 } 789 } 790 } 791 792 public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) { 793 mEngine.sendQuotaExceeded(backupDataBytes, quotaBytes); 794 } 795 796 // If preflight succeeded, returns positive number - preflight size, 797 // otherwise return negative error code. 798 long getPreflightResultBlocking() { 799 try { 800 mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 801 if (mIsCancelled) { 802 return BackupManager.ERROR_BACKUP_CANCELLED; 803 } 804 if (mPreflightResult == BackupTransport.TRANSPORT_OK) { 805 return mPreflight.getExpectedSizeOrErrorCode(); 806 } else { 807 return mPreflightResult; 808 } 809 } catch (InterruptedException e) { 810 return BackupTransport.AGENT_ERROR; 811 } 812 } 813 814 int getBackupResultBlocking() { 815 try { 816 mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 817 if (mIsCancelled) { 818 return BackupManager.ERROR_BACKUP_CANCELLED; 819 } 820 return mBackupResult; 821 } catch (InterruptedException e) { 822 return BackupTransport.AGENT_ERROR; 823 } 824 } 825 826 827 // BackupRestoreTask interface: specifically, timeout detection 828 829 @Override 830 public void execute() { /* intentionally empty */ } 831 832 @Override 833 public void operationComplete(long result) { /* intentionally empty */ } 834 835 @Override 836 public void handleCancel(boolean cancelAll) { 837 if (DEBUG) { 838 Slog.w(TAG, "Full backup cancel of " + mTarget.packageName); 839 } 840 841 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 842 BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL, 843 mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null); 844 mIsCancelled = true; 845 // Cancel tasks spun off by this task. 846 backupManagerService.handleCancel(mEphemeralToken, cancelAll); 847 backupManagerService.tearDownAgentAndKill(mTarget.applicationInfo); 848 // Free up everyone waiting on this task and its children. 849 mPreflightLatch.countDown(); 850 mBackupLatch.countDown(); 851 // We are done with this operation. 852 backupManagerService.removeOperation(mCurrentOpToken); 853 } 854 } 855} 856