Home | History | Annotate | Download | only in internal
      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