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