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.BackupManagerService.DEBUG;
20import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
21import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
22import static com.android.server.backup.BackupManagerService.OP_PENDING;
23import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
24import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
25
26import android.annotation.Nullable;
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.internal.util.Preconditions;
46import com.android.server.EventLogTags;
47import com.android.server.backup.BackupAgentTimeoutParameters;
48import com.android.server.backup.BackupRestoreTask;
49import com.android.server.backup.FullBackupJob;
50import com.android.server.backup.BackupManagerService;
51import com.android.server.backup.TransportManager;
52import com.android.server.backup.internal.OnTaskFinishedListener;
53import com.android.server.backup.internal.Operation;
54import com.android.server.backup.transport.TransportClient;
55import com.android.server.backup.transport.TransportNotAvailableException;
56import com.android.server.backup.utils.AppBackupUtils;
57import com.android.server.backup.utils.BackupManagerMonitorUtils;
58import com.android.server.backup.utils.BackupObserverUtils;
59
60import java.io.FileInputStream;
61import java.io.FileOutputStream;
62import java.io.IOException;
63import java.util.ArrayList;
64import java.util.concurrent.CountDownLatch;
65import java.util.concurrent.TimeUnit;
66import 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 */
97public 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