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