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