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