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