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.internal; 18 19 import static com.android.server.backup.BackupManagerService.DEBUG; 20 import static com.android.server.backup.BackupManagerService.DEBUG_BACKUP_TRACE; 21 import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE; 22 import static com.android.server.backup.BackupManagerService.MORE_DEBUG; 23 import static com.android.server.backup.BackupManagerService.OP_PENDING; 24 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP; 25 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT; 26 import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL; 27 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT; 28 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP; 29 30 import android.annotation.Nullable; 31 import android.app.ApplicationThreadConstants; 32 import android.app.IBackupAgent; 33 import android.app.backup.BackupDataInput; 34 import android.app.backup.BackupDataOutput; 35 import android.app.backup.BackupManager; 36 import android.app.backup.BackupManagerMonitor; 37 import android.app.backup.BackupTransport; 38 import android.app.backup.IBackupManagerMonitor; 39 import android.app.backup.IBackupObserver; 40 import android.content.pm.ApplicationInfo; 41 import android.content.pm.PackageInfo; 42 import android.content.pm.PackageManager; 43 import android.content.pm.PackageManager.NameNotFoundException; 44 import android.os.Message; 45 import android.os.ParcelFileDescriptor; 46 import android.os.RemoteException; 47 import android.os.SELinux; 48 import android.os.UserHandle; 49 import android.os.WorkSource; 50 import android.system.ErrnoException; 51 import android.system.Os; 52 import android.util.EventLog; 53 import android.util.Slog; 54 55 import com.android.internal.annotations.GuardedBy; 56 import com.android.internal.backup.IBackupTransport; 57 import com.android.internal.util.Preconditions; 58 import com.android.server.AppWidgetBackupBridge; 59 import com.android.server.EventLogTags; 60 import com.android.server.backup.BackupAgentTimeoutParameters; 61 import com.android.server.backup.BackupRestoreTask; 62 import com.android.server.backup.DataChangedJournal; 63 import com.android.server.backup.KeyValueBackupJob; 64 import com.android.server.backup.PackageManagerBackupAgent; 65 import com.android.server.backup.BackupManagerService; 66 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask; 67 import com.android.server.backup.transport.TransportClient; 68 import com.android.server.backup.transport.TransportUtils; 69 import com.android.server.backup.utils.AppBackupUtils; 70 import com.android.server.backup.utils.BackupManagerMonitorUtils; 71 import com.android.server.backup.utils.BackupObserverUtils; 72 73 import java.io.DataInputStream; 74 import java.io.DataOutputStream; 75 import java.io.File; 76 import java.io.FileDescriptor; 77 import java.io.FileInputStream; 78 import java.io.FileOutputStream; 79 import java.io.IOException; 80 import java.security.MessageDigest; 81 import java.security.NoSuchAlgorithmException; 82 import java.util.ArrayList; 83 import java.util.List; 84 import java.util.Objects; 85 import java.util.concurrent.CountDownLatch; 86 87 /** 88 * This class handles the process of backing up a given list of key/value backup packages. 89 * Also takes in a list of pending dolly backups and kicks them off when key/value backups 90 * are done. 91 * 92 * Flow: 93 * If required, backup @pm@. 94 * For each pending key/value backup package: 95 * - Bind to agent. 96 * - Call agent.doBackup() 97 * - Wait either for cancel/timeout or operationComplete() callback from the agent. 98 * Start task to perform dolly backups. 99 * 100 * There are three entry points into this class: 101 * - execute() [Called from the handler thread] 102 * - operationComplete(long result) [Called from the handler thread] 103 * - handleCancel(boolean cancelAll) [Can be called from any thread] 104 * These methods synchronize on mCancelLock. 105 * 106 * Interaction with mCurrentOperations: 107 * - An entry for this task is put into mCurrentOperations for the entire lifetime of the 108 * task. This is useful to cancel the task if required. 109 * - An ephemeral entry is put into mCurrentOperations each time we are waiting on for 110 * response from a backup agent. This is used to plumb timeouts and completion callbacks. 111 */ 112 public class PerformBackupTask implements BackupRestoreTask { 113 private static final String TAG = "PerformBackupTask"; 114 115 private BackupManagerService backupManagerService; 116 private final Object mCancelLock = new Object(); 117 118 private ArrayList<BackupRequest> mQueue; 119 private ArrayList<BackupRequest> mOriginalQueue; 120 private File mStateDir; 121 @Nullable private DataChangedJournal mJournal; 122 private BackupState mCurrentState; 123 private List<String> mPendingFullBackups; 124 private IBackupObserver mObserver; 125 private IBackupManagerMonitor mMonitor; 126 127 private final TransportClient mTransportClient; 128 private final OnTaskFinishedListener mListener; 129 private final PerformFullTransportBackupTask mFullBackupTask; 130 private final int mCurrentOpToken; 131 private volatile int mEphemeralOpToken; 132 133 // carried information about the current in-flight operation 134 private IBackupAgent mAgentBinder; 135 private PackageInfo mCurrentPackage; 136 private File mSavedStateName; 137 private File mBackupDataName; 138 private File mNewStateName; 139 private ParcelFileDescriptor mSavedState; 140 private ParcelFileDescriptor mBackupData; 141 private ParcelFileDescriptor mNewState; 142 private int mStatus; 143 private boolean mFinished; 144 private final boolean mUserInitiated; 145 private final boolean mNonIncremental; 146 private final BackupAgentTimeoutParameters mAgentTimeoutParameters; 147 148 private volatile boolean mCancelAll; 149 150 public PerformBackupTask(BackupManagerService backupManagerService, 151 TransportClient transportClient, String dirName, 152 ArrayList<BackupRequest> queue, @Nullable DataChangedJournal journal, 153 IBackupObserver observer, IBackupManagerMonitor monitor, 154 @Nullable OnTaskFinishedListener listener, List<String> pendingFullBackups, 155 boolean userInitiated, boolean nonIncremental) { 156 this.backupManagerService = backupManagerService; 157 mTransportClient = transportClient; 158 mOriginalQueue = queue; 159 mQueue = new ArrayList<>(); 160 mJournal = journal; 161 mObserver = observer; 162 mMonitor = monitor; 163 mListener = (listener != null) ? listener : OnTaskFinishedListener.NOP; 164 mPendingFullBackups = pendingFullBackups; 165 mUserInitiated = userInitiated; 166 mNonIncremental = nonIncremental; 167 mAgentTimeoutParameters = Preconditions.checkNotNull( 168 backupManagerService.getAgentTimeoutParameters(), 169 "Timeout parameters cannot be null"); 170 171 mStateDir = new File(backupManagerService.getBaseStateDir(), dirName); 172 mCurrentOpToken = backupManagerService.generateRandomIntegerToken(); 173 174 mFinished = false; 175 176 synchronized (backupManagerService.getCurrentOpLock()) { 177 if (backupManagerService.isBackupOperationInProgress()) { 178 if (DEBUG) { 179 Slog.d(TAG, "Skipping backup since one is already in progress."); 180 } 181 mCancelAll = true; 182 mFullBackupTask = null; 183 mCurrentState = BackupState.FINAL; 184 backupManagerService.addBackupTrace("Skipped. Backup already in progress."); 185 } else { 186 mCurrentState = BackupState.INITIAL; 187 CountDownLatch latch = new CountDownLatch(1); 188 String[] fullBackups = 189 mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]); 190 mFullBackupTask = 191 new PerformFullTransportBackupTask(backupManagerService, 192 transportClient, 193 /*fullBackupRestoreObserver*/ null, 194 fullBackups, /*updateSchedule*/ false, /*runningJob*/ null, 195 latch, 196 mObserver, mMonitor, mListener, mUserInitiated); 197 198 registerTask(); 199 backupManagerService.addBackupTrace("STATE => INITIAL"); 200 } 201 } 202 } 203 204 /** 205 * Put this task in the repository of running tasks. 206 */ 207 private void registerTask() { 208 synchronized (backupManagerService.getCurrentOpLock()) { 209 backupManagerService.getCurrentOperations().put( 210 mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP)); 211 } 212 } 213 214 /** 215 * Remove this task from repository of running tasks. 216 */ 217 private void unregisterTask() { 218 backupManagerService.removeOperation(mCurrentOpToken); 219 } 220 221 // Main entry point: perform one chunk of work, updating the state as appropriate 222 // and reposting the next chunk to the primary backup handler thread. 223 @Override 224 @GuardedBy("mCancelLock") 225 public void execute() { 226 synchronized (mCancelLock) { 227 switch (mCurrentState) { 228 case INITIAL: 229 beginBackup(); 230 break; 231 232 case BACKUP_PM: 233 backupPm(); 234 break; 235 236 case RUNNING_QUEUE: 237 invokeNextAgent(); 238 break; 239 240 case FINAL: 241 if (!mFinished) { 242 finalizeBackup(); 243 } else { 244 Slog.e(TAG, "Duplicate finish of K/V pass"); 245 } 246 break; 247 } 248 } 249 } 250 251 // We're starting a backup pass. Initialize the transport if we haven't already. 252 private void beginBackup() { 253 if (DEBUG_BACKUP_TRACE) { 254 backupManagerService.clearBackupTrace(); 255 StringBuilder b = new StringBuilder(256); 256 b.append("beginBackup: ["); 257 for (BackupRequest req : mOriginalQueue) { 258 b.append(' '); 259 b.append(req.packageName); 260 } 261 b.append(" ]"); 262 backupManagerService.addBackupTrace(b.toString()); 263 } 264 265 mAgentBinder = null; 266 mStatus = BackupTransport.TRANSPORT_OK; 267 268 // Sanity check: if the queue is empty we have no work to do. 269 if (mOriginalQueue.isEmpty() && mPendingFullBackups.isEmpty()) { 270 Slog.w(TAG, "Backup begun with an empty queue - nothing to do."); 271 backupManagerService.addBackupTrace("queue empty at begin"); 272 BackupObserverUtils.sendBackupFinished(mObserver, BackupManager.SUCCESS); 273 executeNextState(BackupState.FINAL); 274 return; 275 } 276 277 // We need to retain the original queue contents in case of transport 278 // failure, but we want a working copy that we can manipulate along 279 // the way. 280 mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone(); 281 282 // When the transport is forcing non-incremental key/value payloads, we send the 283 // metadata only if it explicitly asks for it. 284 boolean skipPm = mNonIncremental; 285 286 // The app metadata pseudopackage might also be represented in the 287 // backup queue if apps have been added/removed since the last time 288 // we performed a backup. Drop it from the working queue now that 289 // we're committed to evaluating it for backup regardless. 290 for (int i = 0; i < mQueue.size(); i++) { 291 if (PACKAGE_MANAGER_SENTINEL.equals( 292 mQueue.get(i).packageName)) { 293 if (MORE_DEBUG) { 294 Slog.i(TAG, "Metadata in queue; eliding"); 295 } 296 mQueue.remove(i); 297 skipPm = false; 298 break; 299 } 300 } 301 302 if (DEBUG) { 303 Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); 304 } 305 File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); 306 try { 307 IBackupTransport transport = mTransportClient.connectOrThrow("PBT.beginBackup()"); 308 final String transportName = transport.transportDirName(); 309 EventLog.writeEvent(EventLogTags.BACKUP_START, transportName); 310 311 // If we haven't stored package manager metadata yet, we must init the transport. 312 if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) { 313 Slog.i(TAG, "Initializing (wiping) backup state and transport storage"); 314 backupManagerService.addBackupTrace("initializing transport " + transportName); 315 backupManagerService.resetBackupState(mStateDir); // Just to make sure. 316 mStatus = transport.initializeDevice(); 317 318 backupManagerService.addBackupTrace("transport.initializeDevice() == " + mStatus); 319 if (mStatus == BackupTransport.TRANSPORT_OK) { 320 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 321 } else { 322 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 323 Slog.e(TAG, "Transport error in initializeDevice()"); 324 } 325 } 326 327 if (skipPm) { 328 Slog.d(TAG, "Skipping backup of package metadata."); 329 executeNextState(BackupState.RUNNING_QUEUE); 330 } else { 331 // As the package manager is running here in the system process we can just set up 332 // its agent directly. Thus we always run this pass because it's cheap and this way 333 // we guarantee that we don't get out of step even if we're selecting among various 334 // transports at run time. 335 if (mStatus == BackupTransport.TRANSPORT_OK) { 336 executeNextState(BackupState.BACKUP_PM); 337 } 338 } 339 } catch (Exception e) { 340 Slog.e(TAG, "Error in backup thread during init", e); 341 backupManagerService.addBackupTrace("Exception in backup thread during init: " + e); 342 mStatus = BackupTransport.TRANSPORT_ERROR; 343 } finally { 344 // If we've succeeded so far, we will move to the BACKUP_PM state. If something has gone 345 // wrong then that won't have happen so cleanup. 346 backupManagerService.addBackupTrace("exiting prelim: " + mStatus); 347 if (mStatus != BackupTransport.TRANSPORT_OK) { 348 // if things went wrong at this point, we need to 349 // restage everything and try again later. 350 backupManagerService.resetBackupState(mStateDir); // Just to make sure. 351 // In case of any other error, it's backup transport error. 352 BackupObserverUtils.sendBackupFinished(mObserver, 353 BackupManager.ERROR_TRANSPORT_ABORTED); 354 executeNextState(BackupState.FINAL); 355 } 356 } 357 } 358 359 private void backupPm() { 360 try { 361 // The package manager doesn't have a proper <application> etc, but since it's running 362 // here in the system process we can just set up its agent directly and use a synthetic 363 // BackupRequest. 364 PackageManagerBackupAgent pmAgent = backupManagerService.makeMetadataAgent(); 365 mStatus = invokeAgentForBackup( 366 PACKAGE_MANAGER_SENTINEL, 367 IBackupAgent.Stub.asInterface(pmAgent.onBind())); 368 backupManagerService.addBackupTrace("PMBA invoke: " + mStatus); 369 370 // Because the PMBA is a local instance, it has already executed its backup callback and 371 // returned. Blow away the lingering (spurious) pending timeout message for it. 372 backupManagerService.getBackupHandler().removeMessages( 373 MSG_BACKUP_OPERATION_TIMEOUT); 374 } catch (Exception e) { 375 Slog.e(TAG, "Error in backup thread during pm", e); 376 backupManagerService.addBackupTrace("Exception in backup thread during pm: " + e); 377 mStatus = BackupTransport.TRANSPORT_ERROR; 378 } finally { 379 // If we've succeeded so far, invokeAgentForBackup() will have run the PM 380 // metadata and its completion/timeout callback will continue the state 381 // machine chain. If it failed that won't happen; we handle that now. 382 backupManagerService.addBackupTrace("exiting backupPm: " + mStatus); 383 if (mStatus != BackupTransport.TRANSPORT_OK) { 384 // if things went wrong at this point, we need to 385 // restage everything and try again later. 386 backupManagerService.resetBackupState(mStateDir); // Just to make sure. 387 BackupObserverUtils.sendBackupFinished(mObserver, 388 invokeAgentToObserverError(mStatus)); 389 executeNextState(BackupState.FINAL); 390 } 391 } 392 } 393 394 private int invokeAgentToObserverError(int error) { 395 if (error == BackupTransport.AGENT_ERROR) { 396 return BackupManager.ERROR_AGENT_FAILURE; 397 } else { 398 return BackupManager.ERROR_TRANSPORT_ABORTED; 399 } 400 } 401 402 // Transport has been initialized and the PM metadata submitted successfully 403 // if that was warranted. Now we process the single next thing in the queue. 404 private void invokeNextAgent() { 405 mStatus = BackupTransport.TRANSPORT_OK; 406 backupManagerService.addBackupTrace("invoke q=" + mQueue.size()); 407 408 // Sanity check that we have work to do. If not, skip to the end where 409 // we reestablish the wakelock invariants etc. 410 if (mQueue.isEmpty()) { 411 if (MORE_DEBUG) Slog.i(TAG, "queue now empty"); 412 executeNextState(BackupState.FINAL); 413 return; 414 } 415 416 // pop the entry we're going to process on this step 417 BackupRequest request = mQueue.get(0); 418 mQueue.remove(0); 419 420 Slog.d(TAG, "starting key/value backup of " + request); 421 backupManagerService.addBackupTrace("launch agent for " + request.packageName); 422 423 // Verify that the requested app exists; it might be something that 424 // requested a backup but was then uninstalled. The request was 425 // journalled and rather than tamper with the journal it's safer 426 // to sanity-check here. This also gives us the classname of the 427 // package's backup agent. 428 try { 429 PackageManager pm = backupManagerService.getPackageManager(); 430 mCurrentPackage = pm.getPackageInfo(request.packageName, 431 PackageManager.GET_SIGNING_CERTIFICATES); 432 if (!AppBackupUtils.appIsEligibleForBackup(mCurrentPackage.applicationInfo, pm)) { 433 // The manifest has changed but we had a stale backup request pending. 434 // This won't happen again because the app won't be requesting further 435 // backups. 436 Slog.i(TAG, "Package " + request.packageName 437 + " no longer supports backup; skipping"); 438 backupManagerService.addBackupTrace("skipping - not eligible, completion is noop"); 439 // Shouldn't happen in case of requested backup, as pre-check was done in 440 // #requestBackup(), except to app update done concurrently 441 BackupObserverUtils.sendBackupOnPackageResult(mObserver, 442 mCurrentPackage.packageName, 443 BackupManager.ERROR_BACKUP_NOT_ALLOWED); 444 executeNextState(BackupState.RUNNING_QUEUE); 445 return; 446 } 447 448 if (AppBackupUtils.appGetsFullBackup(mCurrentPackage)) { 449 // It's possible that this app *formerly* was enqueued for key/value backup, 450 // but has since been updated and now only supports the full-data path. 451 // Don't proceed with a key/value backup for it in this case. 452 Slog.i(TAG, "Package " + request.packageName 453 + " requests full-data rather than key/value; skipping"); 454 backupManagerService.addBackupTrace( 455 "skipping - fullBackupOnly, completion is noop"); 456 // Shouldn't happen in case of requested backup, as pre-check was done in 457 // #requestBackup() 458 BackupObserverUtils.sendBackupOnPackageResult(mObserver, 459 mCurrentPackage.packageName, 460 BackupManager.ERROR_BACKUP_NOT_ALLOWED); 461 executeNextState(BackupState.RUNNING_QUEUE); 462 return; 463 } 464 465 if (AppBackupUtils.appIsStopped(mCurrentPackage.applicationInfo)) { 466 // The app has been force-stopped or cleared or just installed, 467 // and not yet launched out of that state, so just as it won't 468 // receive broadcasts, we won't run it for backup. 469 backupManagerService.addBackupTrace("skipping - stopped"); 470 BackupObserverUtils.sendBackupOnPackageResult(mObserver, 471 mCurrentPackage.packageName, 472 BackupManager.ERROR_BACKUP_NOT_ALLOWED); 473 executeNextState(BackupState.RUNNING_QUEUE); 474 return; 475 } 476 477 IBackupAgent agent = null; 478 try { 479 backupManagerService.getWakelock().setWorkSource( 480 new WorkSource(mCurrentPackage.applicationInfo.uid)); 481 agent = backupManagerService.bindToAgentSynchronous(mCurrentPackage.applicationInfo, 482 ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL); 483 backupManagerService.addBackupTrace("agent bound; a? = " + (agent != null)); 484 if (agent != null) { 485 mAgentBinder = agent; 486 mStatus = invokeAgentForBackup(request.packageName, agent); 487 // at this point we'll either get a completion callback from the 488 // agent, or a timeout message on the main handler. either way, we're 489 // done here as long as we're successful so far. 490 } else { 491 // Timeout waiting for the agent 492 mStatus = BackupTransport.AGENT_ERROR; 493 } 494 } catch (SecurityException ex) { 495 // Try for the next one. 496 Slog.d(TAG, "error in bind/backup", ex); 497 mStatus = BackupTransport.AGENT_ERROR; 498 backupManagerService.addBackupTrace("agent SE"); 499 } 500 } catch (NameNotFoundException e) { 501 Slog.d(TAG, "Package does not exist; skipping"); 502 backupManagerService.addBackupTrace("no such package"); 503 mStatus = BackupTransport.AGENT_UNKNOWN; 504 } finally { 505 backupManagerService.getWakelock().setWorkSource(null); 506 507 // If there was an agent error, no timeout/completion handling will occur. 508 // That means we need to direct to the next state ourselves. 509 if (mStatus != BackupTransport.TRANSPORT_OK) { 510 BackupState nextState = BackupState.RUNNING_QUEUE; 511 mAgentBinder = null; 512 513 // An agent-level failure means we reenqueue this one agent for 514 // a later retry, but otherwise proceed normally. 515 if (mStatus == BackupTransport.AGENT_ERROR) { 516 if (MORE_DEBUG) { 517 Slog.i(TAG, "Agent failure for " + request.packageName 518 + " - restaging"); 519 } 520 backupManagerService.dataChangedImpl(request.packageName); 521 mStatus = BackupTransport.TRANSPORT_OK; 522 if (mQueue.isEmpty()) nextState = BackupState.FINAL; 523 BackupObserverUtils 524 .sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName, 525 BackupManager.ERROR_AGENT_FAILURE); 526 } else if (mStatus == BackupTransport.AGENT_UNKNOWN) { 527 // Failed lookup of the app, so we couldn't bring up an agent, but 528 // we're otherwise fine. Just drop it and go on to the next as usual. 529 mStatus = BackupTransport.TRANSPORT_OK; 530 BackupObserverUtils 531 .sendBackupOnPackageResult(mObserver, request.packageName, 532 BackupManager.ERROR_PACKAGE_NOT_FOUND); 533 } else { 534 // Transport-level failure means we reenqueue everything 535 revertAndEndBackup(); 536 nextState = BackupState.FINAL; 537 } 538 539 executeNextState(nextState); 540 } else { 541 // success case 542 backupManagerService.addBackupTrace("expecting completion/timeout callback"); 543 } 544 } 545 } 546 547 private void finalizeBackup() { 548 backupManagerService.addBackupTrace("finishing"); 549 550 // Mark packages that we didn't backup (because backup was cancelled, etc.) as needing 551 // backup. 552 for (BackupRequest req : mQueue) { 553 backupManagerService.dataChangedImpl(req.packageName); 554 } 555 556 // Either backup was successful, in which case we of course do not need 557 // this pass's journal any more; or it failed, in which case we just 558 // re-enqueued all of these packages in the current active journal. 559 // Either way, we no longer need this pass's journal. 560 if (mJournal != null && !mJournal.delete()) { 561 Slog.e(TAG, "Unable to remove backup journal file " + mJournal); 562 } 563 564 // If everything actually went through and this is the first time we've 565 // done a backup, we can now record what the current backup dataset token 566 // is. 567 String callerLogString = "PBT.finalizeBackup()"; 568 if ((backupManagerService.getCurrentToken() == 0) && (mStatus 569 == BackupTransport.TRANSPORT_OK)) { 570 backupManagerService.addBackupTrace("success; recording token"); 571 try { 572 IBackupTransport transport = 573 mTransportClient.connectOrThrow(callerLogString); 574 backupManagerService.setCurrentToken(transport.getCurrentRestoreSet()); 575 backupManagerService.writeRestoreTokens(); 576 } catch (Exception e) { 577 // nothing for it at this point, unfortunately, but this will be 578 // recorded the next time we fully succeed. 579 Slog.e(TAG, "Transport threw reporting restore set: " + e.getMessage()); 580 backupManagerService.addBackupTrace("transport threw returning token"); 581 } 582 } 583 584 // Set up the next backup pass - at this point we can set mBackupRunning 585 // to false to allow another pass to fire, because we're done with the 586 // state machine sequence and the wakelock is refcounted. 587 synchronized (backupManagerService.getQueueLock()) { 588 backupManagerService.setBackupRunning(false); 589 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { 590 // Make sure we back up everything and perform the one-time init 591 if (MORE_DEBUG) { 592 Slog.d(TAG, "Server requires init; rerunning"); 593 } 594 backupManagerService.addBackupTrace("init required; rerunning"); 595 try { 596 String name = backupManagerService.getTransportManager() 597 .getTransportName(mTransportClient.getTransportComponent()); 598 backupManagerService.getPendingInits().add(name); 599 } catch (Exception e) { 600 Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage()); 601 // swallow it and proceed; we don't rely on this 602 } 603 clearMetadata(); 604 backupManagerService.backupNow(); 605 } 606 } 607 608 backupManagerService.clearBackupTrace(); 609 610 unregisterTask(); 611 612 if (!mCancelAll && mStatus == BackupTransport.TRANSPORT_OK && 613 mPendingFullBackups != null && !mPendingFullBackups.isEmpty()) { 614 Slog.d(TAG, "Starting full backups for: " + mPendingFullBackups); 615 // Acquiring wakelock for PerformFullTransportBackupTask before its start. 616 backupManagerService.getWakelock().acquire(); 617 // The full-backup task is now responsible for calling onFinish() on mListener, which 618 // was the listener we passed it. 619 (new Thread(mFullBackupTask, "full-transport-requested")).start(); 620 } else if (mCancelAll) { 621 mListener.onFinished(callerLogString); 622 if (mFullBackupTask != null) { 623 mFullBackupTask.unregisterTask(); 624 } 625 BackupObserverUtils.sendBackupFinished(mObserver, 626 BackupManager.ERROR_BACKUP_CANCELLED); 627 } else { 628 mListener.onFinished(callerLogString); 629 mFullBackupTask.unregisterTask(); 630 switch (mStatus) { 631 case BackupTransport.TRANSPORT_OK: 632 case BackupTransport.TRANSPORT_QUOTA_EXCEEDED: 633 case BackupTransport.TRANSPORT_PACKAGE_REJECTED: 634 BackupObserverUtils.sendBackupFinished(mObserver, 635 BackupManager.SUCCESS); 636 break; 637 case BackupTransport.TRANSPORT_NOT_INITIALIZED: 638 BackupObserverUtils.sendBackupFinished(mObserver, 639 BackupManager.ERROR_TRANSPORT_ABORTED); 640 break; 641 case BackupTransport.TRANSPORT_ERROR: 642 default: 643 BackupObserverUtils.sendBackupFinished(mObserver, 644 BackupManager.ERROR_TRANSPORT_ABORTED); 645 break; 646 } 647 } 648 mFinished = true; 649 Slog.i(TAG, "K/V backup pass finished."); 650 // Only once we're entirely finished do we release the wakelock for k/v backup. 651 backupManagerService.getWakelock().release(); 652 } 653 654 // Remove the PM metadata state. This will generate an init on the next pass. 655 private void clearMetadata() { 656 final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); 657 if (pmState.exists()) pmState.delete(); 658 } 659 660 // Invoke an agent's doBackup() and start a timeout message spinning on the main 661 // handler in case it doesn't get back to us. 662 private int invokeAgentForBackup(String packageName, IBackupAgent agent) { 663 if (DEBUG) { 664 Slog.d(TAG, "invokeAgentForBackup on " + packageName); 665 } 666 backupManagerService.addBackupTrace("invoking " + packageName); 667 668 File blankStateName = new File(mStateDir, "blank_state"); 669 mSavedStateName = new File(mStateDir, packageName); 670 mBackupDataName = new File(backupManagerService.getDataDir(), packageName + ".data"); 671 mNewStateName = new File(mStateDir, packageName + ".new"); 672 if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName); 673 674 mSavedState = null; 675 mBackupData = null; 676 mNewState = null; 677 678 boolean callingAgent = false; 679 mEphemeralOpToken = backupManagerService.generateRandomIntegerToken(); 680 try { 681 // Look up the package info & signatures. This is first so that if it 682 // throws an exception, there's no file setup yet that would need to 683 // be unraveled. 684 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) { 685 // The metadata 'package' is synthetic; construct one and make 686 // sure our global state is pointed at it 687 mCurrentPackage = new PackageInfo(); 688 mCurrentPackage.packageName = packageName; 689 } 690 691 // In a full backup, we pass a null ParcelFileDescriptor as 692 // the saved-state "file". For key/value backups we pass the old state if 693 // an incremental backup is required, and a blank state otherwise. 694 mSavedState = ParcelFileDescriptor.open( 695 mNonIncremental ? blankStateName : mSavedStateName, 696 ParcelFileDescriptor.MODE_READ_ONLY | 697 ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary 698 699 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 700 ParcelFileDescriptor.MODE_READ_WRITE | 701 ParcelFileDescriptor.MODE_CREATE | 702 ParcelFileDescriptor.MODE_TRUNCATE); 703 704 if (!SELinux.restorecon(mBackupDataName)) { 705 Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName); 706 } 707 708 mNewState = ParcelFileDescriptor.open(mNewStateName, 709 ParcelFileDescriptor.MODE_READ_WRITE | 710 ParcelFileDescriptor.MODE_CREATE | 711 ParcelFileDescriptor.MODE_TRUNCATE); 712 713 IBackupTransport transport = 714 mTransportClient.connectOrThrow("PBT.invokeAgentForBackup()"); 715 716 final long quota = transport.getBackupQuota(packageName, false /* isFullBackup */); 717 callingAgent = true; 718 719 // Initiate the target's backup pass 720 backupManagerService.addBackupTrace("setting timeout"); 721 long kvBackupAgentTimeoutMillis = 722 mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis(); 723 backupManagerService.prepareOperationTimeout( 724 mEphemeralOpToken, kvBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT); 725 backupManagerService.addBackupTrace("calling agent doBackup()"); 726 727 agent.doBackup( 728 mSavedState, mBackupData, mNewState, quota, mEphemeralOpToken, 729 backupManagerService.getBackupManagerBinder(), transport.getTransportFlags()); 730 } catch (Exception e) { 731 Slog.e(TAG, "Error invoking for backup on " + packageName + ". " + e); 732 backupManagerService.addBackupTrace("exception: " + e); 733 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, 734 e.toString()); 735 errorCleanup(); 736 return callingAgent ? BackupTransport.AGENT_ERROR 737 : BackupTransport.TRANSPORT_ERROR; 738 } finally { 739 if (mNonIncremental) { 740 blankStateName.delete(); 741 } 742 } 743 744 // At this point the agent is off and running. The next thing to happen will 745 // either be a callback from the agent, at which point we'll process its data 746 // for transport, or a timeout. Either way the next phase will happen in 747 // response to the TimeoutHandler interface callbacks. 748 backupManagerService.addBackupTrace("invoke success"); 749 return BackupTransport.TRANSPORT_OK; 750 } 751 752 private void failAgent(IBackupAgent agent, String message) { 753 try { 754 agent.fail(message); 755 } catch (Exception e) { 756 Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName); 757 } 758 } 759 760 // SHA-1 a byte array and return the result in hex 761 private String SHA1Checksum(byte[] input) { 762 final byte[] checksum; 763 try { 764 MessageDigest md = MessageDigest.getInstance("SHA-1"); 765 checksum = md.digest(input); 766 } catch (NoSuchAlgorithmException e) { 767 Slog.e(TAG, "Unable to use SHA-1!"); 768 return "00"; 769 } 770 771 StringBuffer sb = new StringBuffer(checksum.length * 2); 772 for (int i = 0; i < checksum.length; i++) { 773 sb.append(Integer.toHexString(checksum[i])); 774 } 775 return sb.toString(); 776 } 777 778 private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName) 779 throws IOException { 780 // TODO: http://b/22388012 781 byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, 782 UserHandle.USER_SYSTEM); 783 // has the widget state changed since last time? 784 final File widgetFile = new File(mStateDir, pkgName + "_widget"); 785 final boolean priorStateExists = widgetFile.exists(); 786 787 if (MORE_DEBUG) { 788 if (priorStateExists || widgetState != null) { 789 Slog.i(TAG, "Checking widget update: state=" + (widgetState != null) 790 + " prior=" + priorStateExists); 791 } 792 } 793 794 if (!priorStateExists && widgetState == null) { 795 // no prior state, no new state => nothing to do 796 return; 797 } 798 799 // if the new state is not null, we might need to compare checksums to 800 // determine whether to update the widget blob in the archive. If the 801 // widget state *is* null, we know a priori at this point that we simply 802 // need to commit a deletion for it. 803 String newChecksum = null; 804 if (widgetState != null) { 805 newChecksum = SHA1Checksum(widgetState); 806 if (priorStateExists) { 807 final String priorChecksum; 808 try ( 809 FileInputStream fin = new FileInputStream(widgetFile); 810 DataInputStream in = new DataInputStream(fin) 811 ) { 812 priorChecksum = in.readUTF(); 813 } 814 if (Objects.equals(newChecksum, priorChecksum)) { 815 // Same checksum => no state change => don't rewrite the widget data 816 return; 817 } 818 } 819 } // else widget state *became* empty, so we need to commit a deletion 820 821 BackupDataOutput out = new BackupDataOutput(fd); 822 if (widgetState != null) { 823 try ( 824 FileOutputStream fout = new FileOutputStream(widgetFile); 825 DataOutputStream stateOut = new DataOutputStream(fout) 826 ) { 827 stateOut.writeUTF(newChecksum); 828 } 829 830 out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length); 831 out.writeEntityData(widgetState, widgetState.length); 832 } else { 833 // Widget state for this app has been removed; commit a deletion 834 out.writeEntityHeader(KEY_WIDGET_STATE, -1); 835 widgetFile.delete(); 836 } 837 } 838 839 @Override 840 @GuardedBy("mCancelLock") 841 public void operationComplete(long unusedResult) { 842 backupManagerService.removeOperation(mEphemeralOpToken); 843 synchronized (mCancelLock) { 844 // The agent reported back to us! 845 if (mFinished) { 846 Slog.d(TAG, "operationComplete received after task finished."); 847 return; 848 } 849 850 if (mBackupData == null) { 851 // This callback was racing with our timeout, so we've cleaned up the 852 // agent state already and are on to the next thing. We have nothing 853 // further to do here: agent state having been cleared means that we've 854 // initiated the appropriate next operation. 855 final String pkg = (mCurrentPackage != null) 856 ? mCurrentPackage.packageName : "[none]"; 857 if (MORE_DEBUG) { 858 Slog.i(TAG, "Callback after agent teardown: " + pkg); 859 } 860 backupManagerService.addBackupTrace("late opComplete; curPkg = " + pkg); 861 return; 862 } 863 864 final String pkgName = mCurrentPackage.packageName; 865 final long filepos = mBackupDataName.length(); 866 FileDescriptor fd = mBackupData.getFileDescriptor(); 867 try { 868 // If it's a 3rd party app, see whether they wrote any protected keys 869 // and complain mightily if they are attempting shenanigans. 870 if (mCurrentPackage.applicationInfo != null && 871 (mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 872 == 0) { 873 ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName, 874 ParcelFileDescriptor.MODE_READ_ONLY); 875 BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor()); 876 try { 877 while (in.readNextHeader()) { 878 final String key = in.getKey(); 879 if (key != null && key.charAt(0) >= 0xff00) { 880 // Not okay: crash them and bail. 881 failAgent(mAgentBinder, "Illegal backup key: " + key); 882 backupManagerService 883 .addBackupTrace("illegal key " + key + " from " + pkgName); 884 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName, 885 "bad key"); 886 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 887 BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY, 888 mCurrentPackage, 889 BackupManagerMonitor 890 .LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 891 BackupManagerMonitorUtils.putMonitoringExtra(null, 892 BackupManagerMonitor.EXTRA_LOG_ILLEGAL_KEY, 893 key)); 894 backupManagerService.getBackupHandler().removeMessages( 895 MSG_BACKUP_OPERATION_TIMEOUT); 896 BackupObserverUtils 897 .sendBackupOnPackageResult(mObserver, pkgName, 898 BackupManager.ERROR_AGENT_FAILURE); 899 errorCleanup(); 900 // agentErrorCleanup() implicitly executes next state properly 901 return; 902 } 903 in.skipEntityData(); 904 } 905 } finally { 906 if (readFd != null) { 907 readFd.close(); 908 } 909 } 910 } 911 912 // Piggyback the widget state payload, if any 913 writeWidgetPayloadIfAppropriate(fd, pkgName); 914 } catch (IOException e) { 915 // Hard disk error; recovery/failure policy TBD. For now roll back, 916 // but we may want to consider this a transport-level failure (i.e. 917 // we're in such a bad state that we can't contemplate doing backup 918 // operations any more during this pass). 919 Slog.w(TAG, "Unable to save widget state for " + pkgName); 920 try { 921 Os.ftruncate(fd, filepos); 922 } catch (ErrnoException ee) { 923 Slog.w(TAG, "Unable to roll back!"); 924 } 925 } 926 927 // Spin the data off to the transport and proceed with the next stage. 928 if (MORE_DEBUG) { 929 Slog.v(TAG, "operationComplete(): sending data to transport for " 930 + pkgName); 931 } 932 backupManagerService.getBackupHandler().removeMessages(MSG_BACKUP_OPERATION_TIMEOUT); 933 clearAgentState(); 934 backupManagerService.addBackupTrace("operation complete"); 935 936 IBackupTransport transport = mTransportClient.connect("PBT.operationComplete()"); 937 ParcelFileDescriptor backupData = null; 938 mStatus = BackupTransport.TRANSPORT_OK; 939 long size = 0; 940 try { 941 TransportUtils.checkTransportNotNull(transport); 942 size = mBackupDataName.length(); 943 if (size > 0) { 944 boolean isNonIncremental = mSavedStateName.length() == 0; 945 if (mStatus == BackupTransport.TRANSPORT_OK) { 946 backupData = ParcelFileDescriptor.open(mBackupDataName, 947 ParcelFileDescriptor.MODE_READ_ONLY); 948 backupManagerService.addBackupTrace("sending data to transport"); 949 950 int userInitiatedFlag = 951 mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0; 952 int incrementalFlag = 953 isNonIncremental 954 ? BackupTransport.FLAG_NON_INCREMENTAL 955 : BackupTransport.FLAG_INCREMENTAL; 956 int flags = userInitiatedFlag | incrementalFlag; 957 958 mStatus = transport.performBackup(mCurrentPackage, backupData, flags); 959 } 960 961 if (isNonIncremental 962 && mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) { 963 // TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED is only valid if the backup was 964 // incremental, as if the backup is non-incremental there is no state to 965 // clear. This avoids us ending up in a retry loop if the transport always 966 // returns this code. 967 Slog.w(TAG, 968 "Transport requested non-incremental but already the case, error"); 969 backupManagerService.addBackupTrace( 970 "Transport requested non-incremental but already the case, error"); 971 mStatus = BackupTransport.TRANSPORT_ERROR; 972 } 973 974 // TODO - We call finishBackup() for each application backed up, because 975 // we need to know now whether it succeeded or failed. Instead, we should 976 // hold off on finishBackup() until the end, which implies holding off on 977 // renaming *all* the output state files (see below) until that happens. 978 979 backupManagerService.addBackupTrace("data delivered: " + mStatus); 980 if (mStatus == BackupTransport.TRANSPORT_OK) { 981 backupManagerService.addBackupTrace("finishing op on transport"); 982 mStatus = transport.finishBackup(); 983 backupManagerService.addBackupTrace("finished: " + mStatus); 984 } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 985 backupManagerService.addBackupTrace("transport rejected package"); 986 } 987 } else { 988 if (MORE_DEBUG) { 989 Slog.i(TAG, "no backup data written; not calling transport"); 990 } 991 backupManagerService.addBackupTrace("no data to send"); 992 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 993 BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND, 994 mCurrentPackage, 995 BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 996 null); 997 } 998 999 if (mStatus == BackupTransport.TRANSPORT_OK) { 1000 // After successful transport, delete the now-stale data 1001 // and juggle the files so that next time we supply the agent 1002 // with the new state file it just created. 1003 mBackupDataName.delete(); 1004 mNewStateName.renameTo(mSavedStateName); 1005 BackupObserverUtils 1006 .sendBackupOnPackageResult(mObserver, pkgName, BackupManager.SUCCESS); 1007 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size); 1008 backupManagerService.logBackupComplete(pkgName); 1009 } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 1010 // The transport has rejected backup of this specific package. Roll it 1011 // back but proceed with running the rest of the queue. 1012 mBackupDataName.delete(); 1013 mNewStateName.delete(); 1014 BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName, 1015 BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED); 1016 EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected"); 1017 } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { 1018 BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName, 1019 BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED); 1020 EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName); 1021 1022 } else if (mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) { 1023 Slog.i(TAG, "Transport lost data, retrying package"); 1024 backupManagerService.addBackupTrace( 1025 "Transport lost data, retrying package:" + pkgName); 1026 BackupManagerMonitorUtils.monitorEvent( 1027 mMonitor, 1028 BackupManagerMonitor 1029 .LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED, 1030 mCurrentPackage, 1031 BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, 1032 /*extras=*/ null); 1033 1034 mBackupDataName.delete(); 1035 mSavedStateName.delete(); 1036 mNewStateName.delete(); 1037 1038 // Immediately retry the package by adding it back to the front of the queue. 1039 // We cannot add @pm@ to the queue because we back it up separately at the start 1040 // of the backup pass in state BACKUP_PM. Instead we retry this state (see 1041 // below). 1042 if (!PACKAGE_MANAGER_SENTINEL.equals(pkgName)) { 1043 mQueue.add(0, new BackupRequest(pkgName)); 1044 } 1045 1046 } else { 1047 // Actual transport-level failure to communicate the data to the backend 1048 BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName, 1049 BackupManager.ERROR_TRANSPORT_ABORTED); 1050 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); 1051 } 1052 } catch (Exception e) { 1053 BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName, 1054 BackupManager.ERROR_TRANSPORT_ABORTED); 1055 Slog.e(TAG, "Transport error backing up " + pkgName, e); 1056 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); 1057 mStatus = BackupTransport.TRANSPORT_ERROR; 1058 } finally { 1059 try { 1060 if (backupData != null) backupData.close(); 1061 } catch (IOException e) { 1062 } 1063 } 1064 1065 final BackupState nextState; 1066 if (mStatus == BackupTransport.TRANSPORT_OK 1067 || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 1068 // Success or single-package rejection. Proceed with the next app if any, 1069 // otherwise we're done. 1070 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE; 1071 1072 } else if (mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) { 1073 // We want to immediately retry the current package. 1074 if (PACKAGE_MANAGER_SENTINEL.equals(pkgName)) { 1075 nextState = BackupState.BACKUP_PM; 1076 } else { 1077 // This is an ordinary package so we will have added it back into the queue 1078 // above. Thus, we proceed processing the queue. 1079 nextState = BackupState.RUNNING_QUEUE; 1080 } 1081 1082 } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { 1083 if (MORE_DEBUG) { 1084 Slog.d(TAG, "Package " + mCurrentPackage.packageName + 1085 " hit quota limit on k/v backup"); 1086 } 1087 if (mAgentBinder != null) { 1088 try { 1089 TransportUtils.checkTransportNotNull(transport); 1090 long quota = transport.getBackupQuota(mCurrentPackage.packageName, false); 1091 mAgentBinder.doQuotaExceeded(size, quota); 1092 } catch (Exception e) { 1093 Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage()); 1094 } 1095 } 1096 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE; 1097 } else { 1098 // Any other error here indicates a transport-level failure. That means 1099 // we need to halt everything and reschedule everything for next time. 1100 revertAndEndBackup(); 1101 nextState = BackupState.FINAL; 1102 } 1103 1104 executeNextState(nextState); 1105 } 1106 } 1107 1108 1109 @Override 1110 @GuardedBy("mCancelLock") 1111 public void handleCancel(boolean cancelAll) { 1112 backupManagerService.removeOperation(mEphemeralOpToken); 1113 synchronized (mCancelLock) { 1114 if (mFinished) { 1115 // We have already cancelled this operation. 1116 if (MORE_DEBUG) { 1117 Slog.d(TAG, "Ignoring stale cancel. cancelAll=" + cancelAll); 1118 } 1119 return; 1120 } 1121 mCancelAll = cancelAll; 1122 final String logPackageName = (mCurrentPackage != null) 1123 ? mCurrentPackage.packageName 1124 : "no_package_yet"; 1125 Slog.i(TAG, "Cancel backing up " + logPackageName); 1126 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, logPackageName); 1127 backupManagerService.addBackupTrace( 1128 "cancel of " + logPackageName + ", cancelAll=" + cancelAll); 1129 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, 1130 BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL, 1131 mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, 1132 BackupManagerMonitorUtils.putMonitoringExtra(null, 1133 BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, 1134 mCancelAll)); 1135 errorCleanup(); 1136 if (!cancelAll) { 1137 // The current agent either timed out or was cancelled running doBackup(). 1138 // Restage it for the next time we run a backup pass. 1139 // !!! TODO: keep track of failure counts per agent, and blacklist those which 1140 // fail repeatedly (i.e. have proved themselves to be buggy). 1141 executeNextState( 1142 mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE); 1143 backupManagerService.dataChangedImpl(mCurrentPackage.packageName); 1144 } else { 1145 finalizeBackup(); 1146 } 1147 } 1148 } 1149 1150 private void revertAndEndBackup() { 1151 if (MORE_DEBUG) { 1152 Slog.i(TAG, "Reverting backup queue - restaging everything"); 1153 } 1154 backupManagerService.addBackupTrace("transport error; reverting"); 1155 1156 // We want to reset the backup schedule based on whatever the transport suggests 1157 // by way of retry/backoff time. 1158 long delay; 1159 try { 1160 IBackupTransport transport = 1161 mTransportClient.connectOrThrow("PBT.revertAndEndBackup()"); 1162 delay = transport.requestBackupTime(); 1163 } catch (Exception e) { 1164 Slog.w(TAG, "Unable to contact transport for recommended backoff: " + e.getMessage()); 1165 delay = 0; // use the scheduler's default 1166 } 1167 KeyValueBackupJob.schedule(backupManagerService.getContext(), delay, 1168 backupManagerService.getConstants()); 1169 1170 for (BackupRequest request : mOriginalQueue) { 1171 backupManagerService.dataChangedImpl(request.packageName); 1172 } 1173 1174 } 1175 1176 private void errorCleanup() { 1177 mBackupDataName.delete(); 1178 mNewStateName.delete(); 1179 clearAgentState(); 1180 } 1181 1182 // Cleanup common to both success and failure cases 1183 private void clearAgentState() { 1184 try { 1185 if (mSavedState != null) mSavedState.close(); 1186 } catch (IOException e) { 1187 } 1188 try { 1189 if (mBackupData != null) mBackupData.close(); 1190 } catch (IOException e) { 1191 } 1192 try { 1193 if (mNewState != null) mNewState.close(); 1194 } catch (IOException e) { 1195 } 1196 synchronized (backupManagerService.getCurrentOpLock()) { 1197 // Current-operation callback handling requires the validity of these various 1198 // bits of internal state as an invariant of the operation still being live. 1199 // This means we make sure to clear all of the state in unison inside the lock. 1200 backupManagerService.getCurrentOperations().remove(mEphemeralOpToken); 1201 mSavedState = mBackupData = mNewState = null; 1202 } 1203 1204 // If this was a pseudopackage there's no associated Activity Manager state 1205 if (mCurrentPackage.applicationInfo != null) { 1206 backupManagerService.addBackupTrace("unbinding " + mCurrentPackage.packageName); 1207 try { // unbind even on timeout, just in case 1208 backupManagerService.getActivityManager().unbindBackupAgent( 1209 mCurrentPackage.applicationInfo); 1210 } catch (RemoteException e) { /* can't happen; activity manager is local */ } 1211 } 1212 } 1213 1214 private void executeNextState(BackupState nextState) { 1215 if (MORE_DEBUG) { 1216 Slog.i(TAG, " => executing next step on " 1217 + this + " nextState=" + nextState); 1218 } 1219 backupManagerService.addBackupTrace("executeNextState => " + nextState); 1220 mCurrentState = nextState; 1221 Message msg = backupManagerService.getBackupHandler().obtainMessage( 1222 MSG_BACKUP_RESTORE_STEP, this); 1223 backupManagerService.getBackupHandler().sendMessage(msg); 1224 } 1225 } 1226