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