BackupManagerService.java revision 4edbf055ed1e061c2e17e59676d21662de3f506c
1/*
2 * Copyright (C) 2009 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;
18
19import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
20
21import android.app.ActivityManager;
22import android.app.AlarmManager;
23import android.app.AppGlobals;
24import android.app.ApplicationThreadConstants;
25import android.app.IActivityManager;
26import android.app.IBackupAgent;
27import android.app.PackageInstallObserver;
28import android.app.PendingIntent;
29import android.app.backup.BackupAgent;
30import android.app.backup.BackupDataInput;
31import android.app.backup.BackupDataOutput;
32import android.app.backup.BackupManager;
33import android.app.backup.BackupManagerMonitor;
34import android.app.backup.BackupProgress;
35import android.app.backup.BackupTransport;
36import android.app.backup.FullBackup;
37import android.app.backup.FullBackupDataOutput;
38import android.app.backup.IBackupObserver;
39import android.app.backup.IBackupManagerMonitor;
40import android.app.backup.RestoreDescription;
41import android.app.backup.RestoreSet;
42import android.app.backup.IBackupManager;
43import android.app.backup.IFullBackupRestoreObserver;
44import android.app.backup.IRestoreObserver;
45import android.app.backup.IRestoreSession;
46import android.app.backup.ISelectBackupTransportCallback;
47import android.app.backup.SelectBackupTransportCallback;
48import android.content.ActivityNotFoundException;
49import android.content.BroadcastReceiver;
50import android.content.ComponentName;
51import android.content.ContentResolver;
52import android.content.Context;
53import android.content.Intent;
54import android.content.IntentFilter;
55import android.content.ServiceConnection;
56import android.content.pm.ApplicationInfo;
57import android.content.pm.IPackageDataObserver;
58import android.content.pm.IPackageDeleteObserver;
59import android.content.pm.IPackageManager;
60import android.content.pm.PackageInfo;
61import android.content.pm.PackageManager;
62import android.content.pm.PackageManager.NameNotFoundException;
63import android.content.pm.Signature;
64import android.database.ContentObserver;
65import android.net.Uri;
66import android.os.Binder;
67import android.os.Build;
68import android.os.Bundle;
69import android.os.Environment;
70import android.os.Environment.UserEnvironment;
71import android.os.Handler;
72import android.os.HandlerThread;
73import android.os.IBinder;
74import android.os.Looper;
75import android.os.Message;
76import android.os.ParcelFileDescriptor;
77import android.os.PowerManager;
78import android.os.Process;
79import android.os.RemoteException;
80import android.os.SELinux;
81import android.os.ServiceManager;
82import android.os.SystemClock;
83import android.os.UserHandle;
84import android.os.WorkSource;
85import android.os.storage.IStorageManager;
86import android.os.storage.StorageManager;
87import android.provider.Settings;
88import android.system.ErrnoException;
89import android.system.Os;
90import android.text.TextUtils;
91import android.util.AtomicFile;
92import android.util.EventLog;
93import android.util.Log;
94import android.util.Pair;
95import android.util.Slog;
96import android.util.SparseArray;
97import android.util.StringBuilderPrinter;
98
99import com.android.internal.annotations.GuardedBy;
100import com.android.internal.backup.IBackupTransport;
101import com.android.internal.backup.IObbBackupService;
102import com.android.server.AppWidgetBackupBridge;
103import com.android.server.EventLogTags;
104import com.android.server.SystemConfig;
105import com.android.server.SystemService;
106import com.android.server.backup.PackageManagerBackupAgent.Metadata;
107
108import libcore.io.IoUtils;
109
110import java.io.BufferedInputStream;
111import java.io.BufferedOutputStream;
112import java.io.ByteArrayInputStream;
113import java.io.ByteArrayOutputStream;
114import java.io.DataInputStream;
115import java.io.DataOutputStream;
116import java.io.EOFException;
117import java.io.File;
118import java.io.FileDescriptor;
119import java.io.FileInputStream;
120import java.io.FileNotFoundException;
121import java.io.FileOutputStream;
122import java.io.IOException;
123import java.io.InputStream;
124import java.io.OutputStream;
125import java.io.PrintWriter;
126import java.io.RandomAccessFile;
127import java.security.InvalidAlgorithmParameterException;
128import java.security.InvalidKeyException;
129import java.security.Key;
130import java.security.MessageDigest;
131import java.security.NoSuchAlgorithmException;
132import java.security.SecureRandom;
133import java.security.spec.InvalidKeySpecException;
134import java.security.spec.KeySpec;
135import java.text.SimpleDateFormat;
136import java.util.ArrayList;
137import java.util.Arrays;
138import java.util.Collections;
139import java.util.Date;
140import java.util.HashMap;
141import java.util.HashSet;
142import java.util.Iterator;
143import java.util.List;
144import java.util.Map.Entry;
145import java.util.Objects;
146import java.util.Random;
147import java.util.Set;
148import java.util.TreeMap;
149import java.util.concurrent.CountDownLatch;
150import java.util.concurrent.TimeUnit;
151import java.util.concurrent.atomic.AtomicBoolean;
152import java.util.concurrent.atomic.AtomicInteger;
153import java.util.concurrent.atomic.AtomicLong;
154import java.util.zip.Deflater;
155import java.util.zip.DeflaterOutputStream;
156import java.util.zip.InflaterInputStream;
157
158import javax.crypto.BadPaddingException;
159import javax.crypto.Cipher;
160import javax.crypto.CipherInputStream;
161import javax.crypto.CipherOutputStream;
162import javax.crypto.IllegalBlockSizeException;
163import javax.crypto.NoSuchPaddingException;
164import javax.crypto.SecretKey;
165import javax.crypto.SecretKeyFactory;
166import javax.crypto.spec.IvParameterSpec;
167import javax.crypto.spec.PBEKeySpec;
168import javax.crypto.spec.SecretKeySpec;
169
170public class BackupManagerService {
171
172    private static final String TAG = "BackupManagerService";
173    static final boolean DEBUG = true;
174    static final boolean MORE_DEBUG = false;
175    static final boolean DEBUG_SCHEDULING = MORE_DEBUG || true;
176
177    // File containing backup-enabled state.  Contains a single byte;
178    // nonzero == enabled.  File missing or contains a zero byte == disabled.
179    static final String BACKUP_ENABLE_FILE = "backup_enabled";
180
181    // System-private key used for backing up an app's widget state.  Must
182    // begin with U+FFxx by convention (we reserve all keys starting
183    // with U+FF00 or higher for system use).
184    static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
185
186    // Historical and current algorithm names
187    static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
188    static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
189
190    // Name and current contents version of the full-backup manifest file
191    //
192    // Manifest version history:
193    //
194    // 1 : initial release
195    static final String BACKUP_MANIFEST_FILENAME = "_manifest";
196    static final int BACKUP_MANIFEST_VERSION = 1;
197
198    // External archive format version history:
199    //
200    // 1 : initial release
201    // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
202    // 3 : introduced "_meta" metadata file; no other format change per se
203    // 4 : added support for new device-encrypted storage locations
204    static final int BACKUP_FILE_VERSION = 4;
205    static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
206    static final int BACKUP_PW_FILE_VERSION = 2;
207    static final String BACKUP_METADATA_FILENAME = "_meta";
208    static final int BACKUP_METADATA_VERSION = 1;
209    static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
210    static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
211
212    static final String SETTINGS_PACKAGE = "com.android.providers.settings";
213    static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
214    static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
215
216    // Retry interval for clear/init when the transport is unavailable
217    private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
218
219    private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
220    private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
221    private static final int MSG_RUN_BACKUP = 1;
222    private static final int MSG_RUN_ADB_BACKUP = 2;
223    private static final int MSG_RUN_RESTORE = 3;
224    private static final int MSG_RUN_CLEAR = 4;
225    private static final int MSG_RUN_INITIALIZE = 5;
226    private static final int MSG_RUN_GET_RESTORE_SETS = 6;
227    private static final int MSG_RESTORE_SESSION_TIMEOUT = 8;
228    private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
229    private static final int MSG_RUN_ADB_RESTORE = 10;
230    private static final int MSG_RETRY_INIT = 11;
231    private static final int MSG_RETRY_CLEAR = 12;
232    private static final int MSG_WIDGET_BROADCAST = 13;
233    private static final int MSG_RUN_FULL_TRANSPORT_BACKUP = 14;
234    private static final int MSG_REQUEST_BACKUP = 15;
235    private static final int MSG_SCHEDULE_BACKUP_PACKAGE = 16;
236    private static final int MSG_BACKUP_OPERATION_TIMEOUT = 17;
237    private static final int MSG_RESTORE_OPERATION_TIMEOUT = 18;
238
239    // backup task state machine tick
240    static final int MSG_BACKUP_RESTORE_STEP = 20;
241    static final int MSG_OP_COMPLETE = 21;
242
243    // Timeout interval for deciding that a bind or clear-data has taken too long
244    static final long TIMEOUT_INTERVAL = 10 * 1000;
245
246    // Timeout intervals for agent backup & restore operations
247    static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
248    static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
249    static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
250    static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
251    static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
252
253    // User confirmation timeout for a full backup/restore operation.  It's this long in
254    // order to give them time to enter the backup password.
255    static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
256
257    // How long between attempts to perform a full-data backup of any given app
258    static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day
259
260    // If an app is busy when we want to do a full-data backup, how long to defer the retry.
261    // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
262    static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
263    static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
264
265    Context mContext;
266    private PackageManager mPackageManager;
267    IPackageManager mPackageManagerBinder;
268    private IActivityManager mActivityManager;
269    private PowerManager mPowerManager;
270    private AlarmManager mAlarmManager;
271    private IStorageManager mStorageManager;
272    IBackupManager mBackupManagerBinder;
273
274    private final TransportManager mTransportManager;
275
276    boolean mEnabled;   // access to this is synchronized on 'this'
277    boolean mProvisioned;
278    boolean mAutoRestore;
279    PowerManager.WakeLock mWakelock;
280    HandlerThread mHandlerThread;
281    BackupHandler mBackupHandler;
282    PendingIntent mRunBackupIntent, mRunInitIntent;
283    BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
284    // map UIDs to the set of participating packages under that UID
285    final SparseArray<HashSet<String>> mBackupParticipants
286            = new SparseArray<HashSet<String>>();
287    // set of backup services that have pending changes
288    class BackupRequest {
289        public String packageName;
290
291        BackupRequest(String pkgName) {
292            packageName = pkgName;
293        }
294
295        public String toString() {
296            return "BackupRequest{pkg=" + packageName + "}";
297        }
298    }
299    // Backups that we haven't started yet.  Keys are package names.
300    HashMap<String,BackupRequest> mPendingBackups
301            = new HashMap<String,BackupRequest>();
302
303    // Pseudoname that we use for the Package Manager metadata "package"
304    static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
305
306    // locking around the pending-backup management
307    final Object mQueueLock = new Object();
308
309    // The thread performing the sequence of queued backups binds to each app's agent
310    // in succession.  Bind notifications are asynchronously delivered through the
311    // Activity Manager; use this lock object to signal when a requested binding has
312    // completed.
313    final Object mAgentConnectLock = new Object();
314    IBackupAgent mConnectedAgent;
315    volatile boolean mBackupRunning;
316    volatile boolean mConnecting;
317    volatile long mLastBackupPass;
318
319    // For debugging, we maintain a progress trace of operations during backup
320    static final boolean DEBUG_BACKUP_TRACE = true;
321    final List<String> mBackupTrace = new ArrayList<String>();
322
323    // A similar synchronization mechanism around clearing apps' data for restore
324    final Object mClearDataLock = new Object();
325    volatile boolean mClearingData;
326
327    ActiveRestoreSession mActiveRestoreSession;
328
329    // Watch the device provisioning operation during setup
330    ContentObserver mProvisionedObserver;
331
332    // The published binder is actually to a singleton trampoline object that calls
333    // through to the proper code.  This indirection lets us turn down the heavy
334    // implementation object on the fly without disturbing binders that have been
335    // cached elsewhere in the system.
336    static Trampoline sInstance;
337    static Trampoline getInstance() {
338        // Always constructed during system bringup, so no need to lazy-init
339        return sInstance;
340    }
341
342    public static final class Lifecycle extends SystemService {
343
344        public Lifecycle(Context context) {
345            super(context);
346            sInstance = new Trampoline(context);
347        }
348
349        @Override
350        public void onStart() {
351            publishBinderService(Context.BACKUP_SERVICE, sInstance);
352        }
353
354        @Override
355        public void onUnlockUser(int userId) {
356            if (userId == UserHandle.USER_SYSTEM) {
357                sInstance.initialize(userId);
358
359                // Migrate legacy setting
360                if (!backupSettingMigrated(userId)) {
361                    if (DEBUG) {
362                        Slog.i(TAG, "Backup enable apparently not migrated");
363                    }
364                    final ContentResolver r = sInstance.mContext.getContentResolver();
365                    final int enableState = Settings.Secure.getIntForUser(r,
366                            Settings.Secure.BACKUP_ENABLED, -1, userId);
367                    if (enableState >= 0) {
368                        if (DEBUG) {
369                            Slog.i(TAG, "Migrating enable state " + (enableState != 0));
370                        }
371                        writeBackupEnableState(enableState != 0, userId);
372                        Settings.Secure.putStringForUser(r,
373                                Settings.Secure.BACKUP_ENABLED, null, userId);
374                    } else {
375                        if (DEBUG) {
376                            Slog.i(TAG, "Backup not yet configured; retaining null enable state");
377                        }
378                    }
379                }
380
381                try {
382                    sInstance.setBackupEnabled(readBackupEnableState(userId));
383                } catch (RemoteException e) {
384                    // can't happen; it's a local object
385                }
386            }
387        }
388    }
389
390    class ProvisionedObserver extends ContentObserver {
391        public ProvisionedObserver(Handler handler) {
392            super(handler);
393        }
394
395        public void onChange(boolean selfChange) {
396            final boolean wasProvisioned = mProvisioned;
397            final boolean isProvisioned = deviceIsProvisioned();
398            // latch: never unprovision
399            mProvisioned = wasProvisioned || isProvisioned;
400            if (MORE_DEBUG) {
401                Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
402                        + " is=" + isProvisioned + " now=" + mProvisioned);
403            }
404
405            synchronized (mQueueLock) {
406                if (mProvisioned && !wasProvisioned && mEnabled) {
407                    // we're now good to go, so start the backup alarms
408                    if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
409                    KeyValueBackupJob.schedule(mContext);
410                    scheduleNextFullBackupJob(0);
411                }
412            }
413        }
414    }
415
416    class RestoreGetSetsParams {
417        public IBackupTransport transport;
418        public ActiveRestoreSession session;
419        public IRestoreObserver observer;
420        public IBackupManagerMonitor monitor;
421
422        RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
423                IRestoreObserver _observer, IBackupManagerMonitor _monitor) {
424            transport = _transport;
425            session = _session;
426            observer = _observer;
427            monitor = _monitor;
428        }
429    }
430
431    class RestoreParams {
432        public IBackupTransport transport;
433        public String dirName;
434        public IRestoreObserver observer;
435        public IBackupManagerMonitor monitor;
436        public long token;
437        public PackageInfo pkgInfo;
438        public int pmToken; // in post-install restore, the PM's token for this transaction
439        public boolean isSystemRestore;
440        public String[] filterSet;
441
442        /**
443         * Restore a single package; no kill after restore
444         */
445        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
446                IBackupManagerMonitor _monitor, long _token, PackageInfo _pkg) {
447            transport = _transport;
448            dirName = _dirName;
449            observer = _obs;
450            monitor = _monitor;
451            token = _token;
452            pkgInfo = _pkg;
453            pmToken = 0;
454            isSystemRestore = false;
455            filterSet = null;
456        }
457
458        /**
459         * Restore at install: PM token needed, kill after restore
460         */
461        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
462                IBackupManagerMonitor _monitor, long _token, String _pkgName, int _pmToken) {
463            transport = _transport;
464            dirName = _dirName;
465            observer = _obs;
466            monitor = _monitor;
467            token = _token;
468            pkgInfo = null;
469            pmToken = _pmToken;
470            isSystemRestore = false;
471            filterSet = new String[] { _pkgName };
472        }
473
474        /**
475         * Restore everything possible.  This is the form that Setup Wizard or similar
476         * restore UXes use.
477         */
478        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
479                IBackupManagerMonitor _monitor, long _token) {
480            transport = _transport;
481            dirName = _dirName;
482            observer = _obs;
483            monitor = _monitor;
484            token = _token;
485            pkgInfo = null;
486            pmToken = 0;
487            isSystemRestore = true;
488            filterSet = null;
489        }
490
491        /**
492         * Restore some set of packages.  Leave this one up to the caller to specify
493         * whether it's to be considered a system-level restore.
494         */
495        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
496                IBackupManagerMonitor _monitor, long _token,
497                String[] _filterSet, boolean _isSystemRestore) {
498            transport = _transport;
499            dirName = _dirName;
500            observer = _obs;
501            monitor = _monitor;
502            token = _token;
503            pkgInfo = null;
504            pmToken = 0;
505            isSystemRestore = _isSystemRestore;
506            filterSet = _filterSet;
507        }
508    }
509
510    class ClearParams {
511        public IBackupTransport transport;
512        public PackageInfo packageInfo;
513
514        ClearParams(IBackupTransport _transport, PackageInfo _info) {
515            transport = _transport;
516            packageInfo = _info;
517        }
518    }
519
520    class ClearRetryParams {
521        public String transportName;
522        public String packageName;
523
524        ClearRetryParams(String transport, String pkg) {
525            transportName = transport;
526            packageName = pkg;
527        }
528    }
529
530    class FullParams {
531        public ParcelFileDescriptor fd;
532        public final AtomicBoolean latch;
533        public IFullBackupRestoreObserver observer;
534        public String curPassword;     // filled in by the confirmation step
535        public String encryptPassword;
536
537        FullParams() {
538            latch = new AtomicBoolean(false);
539        }
540    }
541
542    class FullBackupParams extends FullParams {
543        public boolean includeApks;
544        public boolean includeObbs;
545        public boolean includeShared;
546        public boolean doWidgets;
547        public boolean allApps;
548        public boolean includeSystem;
549        public boolean doCompress;
550        public String[] packages;
551
552        FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
553                boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem,
554                boolean compress, String[] pkgList) {
555            fd = output;
556            includeApks = saveApks;
557            includeObbs = saveObbs;
558            includeShared = saveShared;
559            doWidgets = alsoWidgets;
560            allApps = doAllApps;
561            includeSystem = doSystem;
562            doCompress = compress;
563            packages = pkgList;
564        }
565    }
566
567    class FullRestoreParams extends FullParams {
568        FullRestoreParams(ParcelFileDescriptor input) {
569            fd = input;
570        }
571    }
572
573    class BackupParams {
574        public IBackupTransport transport;
575        public String dirName;
576        public ArrayList<String> kvPackages;
577        public ArrayList<String> fullPackages;
578        public IBackupObserver observer;
579        public IBackupManagerMonitor monitor;
580        public boolean userInitiated;
581        public boolean nonIncrementalBackup;
582
583        BackupParams(IBackupTransport transport, String dirName, ArrayList<String> kvPackages,
584                ArrayList<String> fullPackages, IBackupObserver observer,
585                IBackupManagerMonitor monitor,boolean userInitiated, boolean nonIncrementalBackup) {
586            this.transport = transport;
587            this.dirName = dirName;
588            this.kvPackages = kvPackages;
589            this.fullPackages = fullPackages;
590            this.observer = observer;
591            this.monitor = monitor;
592            this.userInitiated = userInitiated;
593            this.nonIncrementalBackup = nonIncrementalBackup;
594        }
595    }
596
597    // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
598    // token is the index of the entry in the pending-operations list.
599    static final int OP_PENDING = 0;
600    static final int OP_ACKNOWLEDGED = 1;
601    static final int OP_TIMEOUT = -1;
602
603    // Waiting for backup agent to respond during backup operation.
604    private static final int OP_TYPE_BACKUP_WAIT = 0;
605
606    // Waiting for backup agent to respond during restore operation.
607    private static final int OP_TYPE_RESTORE_WAIT = 1;
608
609    // An entire backup operation spanning multiple packages.
610    private static final int OP_TYPE_BACKUP = 2;
611
612    class Operation {
613        int state;
614        final BackupRestoreTask callback;
615        final int type;
616
617        Operation(int initialState, BackupRestoreTask callbackObj, int type) {
618            state = initialState;
619            callback = callbackObj;
620            this.type = type;
621        }
622    }
623
624    /**
625     * mCurrentOperations contains the list of currently active operations.
626     *
627     * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
628     * An operation wraps a BackupRestoreTask within it.
629     * It's the responsibility of this task to remove the operation from this array.
630     *
631     * A BackupRestore task gets notified of ack/timeout for the operation via
632     * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
633     * on the mCurrentOpLock. {@link BackupManagerService#waitUntilOperationComplete(int)} is
634     * used in various places to 'wait' for notifyAll and detect change of pending state of an
635     * operation. So typically, an operation will be removed from this array by:
636     *   - BackupRestoreTask#handleCancel and
637     *   - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
638     *     these places because waitUntilOperationComplete relies on the operation being present to
639     *     determine its completion status.
640     *
641     * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
642     * cancel backup tasks.
643     */
644    @GuardedBy("mCurrentOpLock")
645    final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>();
646    final Object mCurrentOpLock = new Object();
647    final Random mTokenGenerator = new Random();
648
649    final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
650
651    // Where we keep our journal files and other bookkeeping
652    File mBaseStateDir;
653    File mDataDir;
654    File mJournalDir;
655    File mJournal;
656
657    // Backup password, if any, and the file where it's saved.  What is stored is not the
658    // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but
659    // persisted) salt.  Validation is performed by running the challenge text through the
660    // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches
661    // the saved hash string, then the challenge text matches the originally supplied
662    // password text.
663    private final SecureRandom mRng = new SecureRandom();
664    private String mPasswordHash;
665    private File mPasswordHashFile;
666    private int mPasswordVersion;
667    private File mPasswordVersionFile;
668    private byte[] mPasswordSalt;
669
670    // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys
671    static final int PBKDF2_HASH_ROUNDS = 10000;
672    static final int PBKDF2_KEY_SIZE = 256;     // bits
673    static final int PBKDF2_SALT_SIZE = 512;    // bits
674    static final String ENCRYPTION_ALGORITHM_NAME = "AES-256";
675
676    // Keep a log of all the apps we've ever backed up, and what the
677    // dataset tokens are for both the current backup dataset and
678    // the ancestral dataset.
679    private File mEverStored;
680    HashSet<String> mEverStoredApps = new HashSet<String>();
681
682    static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;  // increment when the schema changes
683    File mTokenFile;
684    Set<String> mAncestralPackages = null;
685    long mAncestralToken = 0;
686    long mCurrentToken = 0;
687
688    // Persistently track the need to do a full init
689    static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
690    HashSet<String> mPendingInits = new HashSet<String>();  // transport names
691
692    // Round-robin queue for scheduling full backup passes
693    static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
694    class FullBackupEntry implements Comparable<FullBackupEntry> {
695        String packageName;
696        long lastBackup;
697
698        FullBackupEntry(String pkg, long when) {
699            packageName = pkg;
700            lastBackup = when;
701        }
702
703        @Override
704        public int compareTo(FullBackupEntry other) {
705            if (lastBackup < other.lastBackup) return -1;
706            else if (lastBackup > other.lastBackup) return 1;
707            else return 0;
708        }
709    }
710
711    File mFullBackupScheduleFile;
712    // If we're running a schedule-driven full backup, this is the task instance doing it
713
714    @GuardedBy("mQueueLock")
715    PerformFullTransportBackupTask mRunningFullBackupTask;
716
717    @GuardedBy("mQueueLock")
718    ArrayList<FullBackupEntry> mFullBackupQueue;
719
720    // Utility: build a new random integer token
721    int generateToken() {
722        int token;
723        do {
724            synchronized (mTokenGenerator) {
725                token = mTokenGenerator.nextInt();
726            }
727        } while (token < 0);
728        return token;
729    }
730
731    // High level policy: apps are generally ineligible for backup if certain conditions apply
732    public static boolean appIsEligibleForBackup(ApplicationInfo app) {
733        // 1. their manifest states android:allowBackup="false"
734        if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
735            return false;
736        }
737
738        // 2. they run as a system-level uid but do not supply their own backup agent
739        if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) {
740            return false;
741        }
742
743        // 3. it is the special shared-storage backup package used for 'adb backup'
744        if (app.packageName.equals(BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE)) {
745            return false;
746        }
747
748        return true;
749    }
750
751    // Checks if the app is in a stopped state, that means it won't receive broadcasts.
752    private static boolean appIsStopped(ApplicationInfo app) {
753        return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0);
754    }
755
756    /* does *not* check overall backup eligibility policy! */
757    private static boolean appGetsFullBackup(PackageInfo pkg) {
758        if (pkg.applicationInfo.backupAgentName != null) {
759            // If it has an agent, it gets full backups only if it says so
760            return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0;
761        }
762
763        // No agent or fullBackupOnly="true" means we do indeed perform full-data backups for it
764        return true;
765    }
766
767    /* adb backup: is this app only capable of doing key/value?  We say otherwise if
768     * the app has a backup agent and does not say fullBackupOnly, *unless* it
769     * is a package that we know _a priori_ explicitly supports both key/value and
770     * full-data backup.
771     */
772    private static boolean appIsKeyValueOnly(PackageInfo pkg) {
773        if ("com.android.providers.settings".equals(pkg.packageName)) {
774            return false;
775        }
776
777        return !appGetsFullBackup(pkg);
778    }
779
780    // ----- Asynchronous backup/restore handler thread -----
781
782    private class BackupHandler extends Handler {
783        public BackupHandler(Looper looper) {
784            super(looper);
785        }
786
787        public void handleMessage(Message msg) {
788
789            switch (msg.what) {
790            case MSG_RUN_BACKUP:
791            {
792                mLastBackupPass = System.currentTimeMillis();
793
794                IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
795                if (transport == null) {
796                    Slog.v(TAG, "Backup requested but no transport available");
797                    synchronized (mQueueLock) {
798                        mBackupRunning = false;
799                    }
800                    mWakelock.release();
801                    break;
802                }
803
804                // snapshot the pending-backup set and work on that
805                ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
806                File oldJournal = mJournal;
807                synchronized (mQueueLock) {
808                    // Do we have any work to do?  Construct the work queue
809                    // then release the synchronization lock to actually run
810                    // the backup.
811                    if (mPendingBackups.size() > 0) {
812                        for (BackupRequest b: mPendingBackups.values()) {
813                            queue.add(b);
814                        }
815                        if (DEBUG) Slog.v(TAG, "clearing pending backups");
816                        mPendingBackups.clear();
817
818                        // Start a new backup-queue journal file too
819                        mJournal = null;
820
821                    }
822                }
823
824                // At this point, we have started a new journal file, and the old
825                // file identity is being passed to the backup processing task.
826                // When it completes successfully, that old journal file will be
827                // deleted.  If we crash prior to that, the old journal is parsed
828                // at next boot and the journaled requests fulfilled.
829                boolean staged = true;
830                if (queue.size() > 0) {
831                    // Spin up a backup state sequence and set it running
832                    try {
833                        String dirName = transport.transportDirName();
834                        PerformBackupTask pbt = new PerformBackupTask(transport, dirName, queue,
835                                oldJournal, null, null, null, false, false /* nonIncremental */);
836                        Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
837                        sendMessage(pbtMessage);
838                    } catch (Exception e) {
839                        // unable to ask the transport its dir name -- transient failure, since
840                        // the above check succeeded.  Try again next time.
841                        Slog.e(TAG, "Transport became unavailable attempting backup");
842                        staged = false;
843                    }
844                } else {
845                    Slog.v(TAG, "Backup requested but nothing pending");
846                    staged = false;
847                }
848
849                if (!staged) {
850                    // if we didn't actually hand off the wakelock, rewind until next time
851                    synchronized (mQueueLock) {
852                        mBackupRunning = false;
853                    }
854                    mWakelock.release();
855                }
856                break;
857            }
858
859            case MSG_BACKUP_RESTORE_STEP:
860            {
861                try {
862                    BackupRestoreTask task = (BackupRestoreTask) msg.obj;
863                    if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing");
864                    task.execute();
865                } catch (ClassCastException e) {
866                    Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);
867                }
868                break;
869            }
870
871            case MSG_OP_COMPLETE:
872            {
873                try {
874                    Pair<BackupRestoreTask, Long> taskWithResult =
875                            (Pair<BackupRestoreTask, Long>) msg.obj;
876                    taskWithResult.first.operationComplete(taskWithResult.second);
877                } catch (ClassCastException e) {
878                    Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
879                }
880                break;
881            }
882
883            case MSG_RUN_ADB_BACKUP:
884            {
885                // TODO: refactor full backup to be a looper-based state machine
886                // similar to normal backup/restore.
887                FullBackupParams params = (FullBackupParams)msg.obj;
888                PerformAdbBackupTask task = new PerformAdbBackupTask(params.fd,
889                        params.observer, params.includeApks, params.includeObbs,
890                        params.includeShared, params.doWidgets,
891                        params.curPassword, params.encryptPassword,
892                        params.allApps, params.includeSystem, params.doCompress,
893                        params.packages, params.latch);
894                (new Thread(task, "adb-backup")).start();
895                break;
896            }
897
898            case MSG_RUN_FULL_TRANSPORT_BACKUP:
899            {
900                PerformFullTransportBackupTask task = (PerformFullTransportBackupTask) msg.obj;
901                (new Thread(task, "transport-backup")).start();
902                break;
903            }
904
905            case MSG_RUN_RESTORE:
906            {
907                RestoreParams params = (RestoreParams)msg.obj;
908                Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
909                BackupRestoreTask task = new PerformUnifiedRestoreTask(params.transport,
910                        params.observer, params.monitor, params.token, params.pkgInfo,
911                        params.pmToken, params.isSystemRestore, params.filterSet);
912                Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
913                sendMessage(restoreMsg);
914                break;
915            }
916
917            case MSG_RUN_ADB_RESTORE:
918            {
919                // TODO: refactor full restore to be a looper-based state machine
920                // similar to normal backup/restore.
921                FullRestoreParams params = (FullRestoreParams)msg.obj;
922                PerformAdbRestoreTask task = new PerformAdbRestoreTask(params.fd,
923                        params.curPassword, params.encryptPassword,
924                        params.observer, params.latch);
925                (new Thread(task, "adb-restore")).start();
926                break;
927            }
928
929            case MSG_RUN_CLEAR:
930            {
931                ClearParams params = (ClearParams)msg.obj;
932                (new PerformClearTask(params.transport, params.packageInfo)).run();
933                break;
934            }
935
936            case MSG_RETRY_CLEAR:
937            {
938                // reenqueues if the transport remains unavailable
939                ClearRetryParams params = (ClearRetryParams)msg.obj;
940                clearBackupData(params.transportName, params.packageName);
941                break;
942            }
943
944            case MSG_RUN_INITIALIZE:
945            {
946                HashSet<String> queue;
947
948                // Snapshot the pending-init queue and work on that
949                synchronized (mQueueLock) {
950                    queue = new HashSet<String>(mPendingInits);
951                    mPendingInits.clear();
952                }
953
954                (new PerformInitializeTask(queue)).run();
955                break;
956            }
957
958            case MSG_RETRY_INIT:
959            {
960                synchronized (mQueueLock) {
961                    recordInitPendingLocked(msg.arg1 != 0, (String)msg.obj);
962                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
963                            mRunInitIntent);
964                }
965                break;
966            }
967
968            case MSG_RUN_GET_RESTORE_SETS:
969            {
970                // Like other async operations, this is entered with the wakelock held
971                RestoreSet[] sets = null;
972                RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
973                try {
974                    sets = params.transport.getAvailableRestoreSets();
975                    // cache the result in the active session
976                    synchronized (params.session) {
977                        params.session.mRestoreSets = sets;
978                    }
979                    if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
980                } catch (Exception e) {
981                    Slog.e(TAG, "Error from transport getting set list: " + e.getMessage());
982                } finally {
983                    if (params.observer != null) {
984                        try {
985                            params.observer.restoreSetsAvailable(sets);
986                        } catch (RemoteException re) {
987                            Slog.e(TAG, "Unable to report listing to observer");
988                        } catch (Exception e) {
989                            Slog.e(TAG, "Restore observer threw: " + e.getMessage());
990                        }
991                    }
992
993                    // Done: reset the session timeout clock
994                    removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
995                    sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
996
997                    mWakelock.release();
998                }
999                break;
1000            }
1001
1002            case MSG_BACKUP_OPERATION_TIMEOUT:
1003            case MSG_RESTORE_OPERATION_TIMEOUT:
1004            {
1005                Slog.d(TAG, "Timeout message received for token=" + Integer.toHexString(msg.arg1));
1006                handleCancel(msg.arg1, false);
1007                break;
1008            }
1009
1010            case MSG_RESTORE_SESSION_TIMEOUT:
1011            {
1012                synchronized (BackupManagerService.this) {
1013                    if (mActiveRestoreSession != null) {
1014                        // Client app left the restore session dangling.  We know that it
1015                        // can't be in the middle of an actual restore operation because
1016                        // the timeout is suspended while a restore is in progress.  Clean
1017                        // up now.
1018                        Slog.w(TAG, "Restore session timed out; aborting");
1019                        mActiveRestoreSession.markTimedOut();
1020                        post(mActiveRestoreSession.new EndRestoreRunnable(
1021                                BackupManagerService.this, mActiveRestoreSession));
1022                    }
1023                }
1024                break;
1025            }
1026
1027            case MSG_FULL_CONFIRMATION_TIMEOUT:
1028            {
1029                synchronized (mFullConfirmations) {
1030                    FullParams params = mFullConfirmations.get(msg.arg1);
1031                    if (params != null) {
1032                        Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
1033
1034                        // Release the waiter; timeout == completion
1035                        signalFullBackupRestoreCompletion(params);
1036
1037                        // Remove the token from the set
1038                        mFullConfirmations.delete(msg.arg1);
1039
1040                        // Report a timeout to the observer, if any
1041                        if (params.observer != null) {
1042                            try {
1043                                params.observer.onTimeout();
1044                            } catch (RemoteException e) {
1045                                /* don't care if the app has gone away */
1046                            }
1047                        }
1048                    } else {
1049                        Slog.d(TAG, "couldn't find params for token " + msg.arg1);
1050                    }
1051                }
1052                break;
1053            }
1054
1055            case MSG_WIDGET_BROADCAST:
1056            {
1057                final Intent intent = (Intent) msg.obj;
1058                mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
1059                break;
1060            }
1061
1062            case MSG_REQUEST_BACKUP:
1063            {
1064                BackupParams params = (BackupParams)msg.obj;
1065                if (MORE_DEBUG) {
1066                    Slog.d(TAG, "MSG_REQUEST_BACKUP observer=" + params.observer);
1067                }
1068                ArrayList<BackupRequest> kvQueue = new ArrayList<>();
1069                for (String packageName : params.kvPackages) {
1070                    kvQueue.add(new BackupRequest(packageName));
1071                }
1072                mBackupRunning = true;
1073                mWakelock.acquire();
1074
1075                PerformBackupTask pbt = new PerformBackupTask(params.transport, params.dirName,
1076                        kvQueue, null, params.observer, params.monitor, params.fullPackages, true,
1077                        params.nonIncrementalBackup);
1078                Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
1079                sendMessage(pbtMessage);
1080                break;
1081            }
1082
1083            case MSG_SCHEDULE_BACKUP_PACKAGE:
1084            {
1085                String pkgName = (String)msg.obj;
1086                if (MORE_DEBUG) {
1087                    Slog.d(TAG, "MSG_SCHEDULE_BACKUP_PACKAGE " + pkgName);
1088                }
1089                dataChangedImpl(pkgName);
1090                break;
1091            }
1092            }
1093        }
1094    }
1095
1096    // ----- Debug-only backup operation trace -----
1097    void addBackupTrace(String s) {
1098        if (DEBUG_BACKUP_TRACE) {
1099            synchronized (mBackupTrace) {
1100                mBackupTrace.add(s);
1101            }
1102        }
1103    }
1104
1105    void clearBackupTrace() {
1106        if (DEBUG_BACKUP_TRACE) {
1107            synchronized (mBackupTrace) {
1108                mBackupTrace.clear();
1109            }
1110        }
1111    }
1112
1113    // ----- Main service implementation -----
1114
1115    public BackupManagerService(Context context, Trampoline parent) {
1116        mContext = context;
1117        mPackageManager = context.getPackageManager();
1118        mPackageManagerBinder = AppGlobals.getPackageManager();
1119        mActivityManager = ActivityManager.getService();
1120
1121        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
1122        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1123        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
1124
1125        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
1126
1127        // spin up the backup/restore handler thread
1128        mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
1129        mHandlerThread.start();
1130        mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
1131
1132        // Set up our bookkeeping
1133        final ContentResolver resolver = context.getContentResolver();
1134        mProvisioned = Settings.Global.getInt(resolver,
1135                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
1136        mAutoRestore = Settings.Secure.getInt(resolver,
1137                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
1138
1139        mProvisionedObserver = new ProvisionedObserver(mBackupHandler);
1140        resolver.registerContentObserver(
1141                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
1142                false, mProvisionedObserver);
1143
1144        // If Encrypted file systems is enabled or disabled, this call will return the
1145        // correct directory.
1146        mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
1147        mBaseStateDir.mkdirs();
1148        if (!SELinux.restorecon(mBaseStateDir)) {
1149            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
1150        }
1151
1152        // This dir on /cache is managed directly in init.rc
1153        mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup_stage");
1154
1155        mPasswordVersion = 1;       // unless we hear otherwise
1156        mPasswordVersionFile = new File(mBaseStateDir, "pwversion");
1157        if (mPasswordVersionFile.exists()) {
1158            FileInputStream fin = null;
1159            DataInputStream in = null;
1160            try {
1161                fin = new FileInputStream(mPasswordVersionFile);
1162                in = new DataInputStream(fin);
1163                mPasswordVersion = in.readInt();
1164            } catch (IOException e) {
1165                Slog.e(TAG, "Unable to read backup pw version");
1166            } finally {
1167                try {
1168                    if (in != null) in.close();
1169                    if (fin != null) fin.close();
1170                } catch (IOException e) {
1171                    Slog.w(TAG, "Error closing pw version files");
1172                }
1173            }
1174        }
1175
1176        mPasswordHashFile = new File(mBaseStateDir, "pwhash");
1177        if (mPasswordHashFile.exists()) {
1178            FileInputStream fin = null;
1179            DataInputStream in = null;
1180            try {
1181                fin = new FileInputStream(mPasswordHashFile);
1182                in = new DataInputStream(new BufferedInputStream(fin));
1183                // integer length of the salt array, followed by the salt,
1184                // then the hex pw hash string
1185                int saltLen = in.readInt();
1186                byte[] salt = new byte[saltLen];
1187                in.readFully(salt);
1188                mPasswordHash = in.readUTF();
1189                mPasswordSalt = salt;
1190            } catch (IOException e) {
1191                Slog.e(TAG, "Unable to read saved backup pw hash");
1192            } finally {
1193                try {
1194                    if (in != null) in.close();
1195                    if (fin != null) fin.close();
1196                } catch (IOException e) {
1197                    Slog.w(TAG, "Unable to close streams");
1198                }
1199            }
1200        }
1201
1202        // Alarm receivers for scheduled backups & initialization operations
1203        mRunBackupReceiver = new RunBackupReceiver();
1204        IntentFilter filter = new IntentFilter();
1205        filter.addAction(RUN_BACKUP_ACTION);
1206        context.registerReceiver(mRunBackupReceiver, filter,
1207                android.Manifest.permission.BACKUP, null);
1208
1209        mRunInitReceiver = new RunInitializeReceiver();
1210        filter = new IntentFilter();
1211        filter.addAction(RUN_INITIALIZE_ACTION);
1212        context.registerReceiver(mRunInitReceiver, filter,
1213                android.Manifest.permission.BACKUP, null);
1214
1215        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
1216        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
1217        mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
1218
1219        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
1220        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
1221        mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
1222
1223        // Set up the backup-request journaling
1224        mJournalDir = new File(mBaseStateDir, "pending");
1225        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
1226        mJournal = null;        // will be created on first use
1227
1228        // Set up the various sorts of package tracking we do
1229        mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
1230        initPackageTracking();
1231
1232        // Build our mapping of uid to backup client services.  This implicitly
1233        // schedules a backup pass on the Package Manager metadata the first
1234        // time anything needs to be backed up.
1235        synchronized (mBackupParticipants) {
1236            addPackageParticipantsLocked(null);
1237        }
1238
1239        // Set up our transport options and initialize the default transport
1240        // TODO: Don't create transports that we don't need to?
1241        SystemConfig systemConfig = SystemConfig.getInstance();
1242        Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
1243
1244        String transport = Settings.Secure.getString(context.getContentResolver(),
1245                Settings.Secure.BACKUP_TRANSPORT);
1246        if (TextUtils.isEmpty(transport)) {
1247            transport = null;
1248        }
1249        String currentTransport = transport;
1250        if (DEBUG) Slog.v(TAG, "Starting with transport " + currentTransport);
1251
1252        mTransportManager = new TransportManager(context, transportWhitelist, currentTransport,
1253                mTransportBoundListener);
1254        mTransportManager.registerAllTransports();
1255
1256        // Now that we know about valid backup participants, parse any
1257        // leftover journal files into the pending backup set
1258        mBackupHandler.post(() -> parseLeftoverJournals());
1259
1260        // Power management
1261        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
1262    }
1263
1264    private class RunBackupReceiver extends BroadcastReceiver {
1265        public void onReceive(Context context, Intent intent) {
1266            if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
1267                synchronized (mQueueLock) {
1268                    if (mPendingInits.size() > 0) {
1269                        // If there are pending init operations, we process those
1270                        // and then settle into the usual periodic backup schedule.
1271                        if (MORE_DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
1272                        try {
1273                            mAlarmManager.cancel(mRunInitIntent);
1274                            mRunInitIntent.send();
1275                        } catch (PendingIntent.CanceledException ce) {
1276                            Slog.e(TAG, "Run init intent cancelled");
1277                            // can't really do more than bail here
1278                        }
1279                    } else {
1280                        // Don't run backups now if we're disabled or not yet
1281                        // fully set up.
1282                        if (mEnabled && mProvisioned) {
1283                            if (!mBackupRunning) {
1284                                if (DEBUG) Slog.v(TAG, "Running a backup pass");
1285
1286                                // Acquire the wakelock and pass it to the backup thread.  it will
1287                                // be released once backup concludes.
1288                                mBackupRunning = true;
1289                                mWakelock.acquire();
1290
1291                                Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
1292                                mBackupHandler.sendMessage(msg);
1293                            } else {
1294                                Slog.i(TAG, "Backup time but one already running");
1295                            }
1296                        } else {
1297                            Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
1298                        }
1299                    }
1300                }
1301            }
1302        }
1303    }
1304
1305    private class RunInitializeReceiver extends BroadcastReceiver {
1306        public void onReceive(Context context, Intent intent) {
1307            if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
1308                synchronized (mQueueLock) {
1309                    if (DEBUG) Slog.v(TAG, "Running a device init");
1310
1311                    // Acquire the wakelock and pass it to the init thread.  it will
1312                    // be released once init concludes.
1313                    mWakelock.acquire();
1314
1315                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
1316                    mBackupHandler.sendMessage(msg);
1317                }
1318            }
1319        }
1320    }
1321
1322    private void initPackageTracking() {
1323        if (MORE_DEBUG) Slog.v(TAG, "` tracking");
1324
1325        // Remember our ancestral dataset
1326        mTokenFile = new File(mBaseStateDir, "ancestral");
1327        try {
1328            RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
1329            int version = tf.readInt();
1330            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
1331                mAncestralToken = tf.readLong();
1332                mCurrentToken = tf.readLong();
1333
1334                int numPackages = tf.readInt();
1335                if (numPackages >= 0) {
1336                    mAncestralPackages = new HashSet<String>();
1337                    for (int i = 0; i < numPackages; i++) {
1338                        String pkgName = tf.readUTF();
1339                        mAncestralPackages.add(pkgName);
1340                    }
1341                }
1342            }
1343            tf.close();
1344        } catch (FileNotFoundException fnf) {
1345            // Probably innocuous
1346            Slog.v(TAG, "No ancestral data");
1347        } catch (IOException e) {
1348            Slog.w(TAG, "Unable to read token file", e);
1349        }
1350
1351        // Keep a log of what apps we've ever backed up.  Because we might have
1352        // rebooted in the middle of an operation that was removing something from
1353        // this log, we sanity-check its contents here and reconstruct it.
1354        mEverStored = new File(mBaseStateDir, "processed");
1355        File tempProcessedFile = new File(mBaseStateDir, "processed.new");
1356
1357        // If we were in the middle of removing something from the ever-backed-up
1358        // file, there might be a transient "processed.new" file still present.
1359        // Ignore it -- we'll validate "processed" against the current package set.
1360        if (tempProcessedFile.exists()) {
1361            tempProcessedFile.delete();
1362        }
1363
1364        // If there are previous contents, parse them out then start a new
1365        // file to continue the recordkeeping.
1366        if (mEverStored.exists()) {
1367            RandomAccessFile temp = null;
1368            RandomAccessFile in = null;
1369
1370            try {
1371                temp = new RandomAccessFile(tempProcessedFile, "rws");
1372                in = new RandomAccessFile(mEverStored, "r");
1373
1374                // Loop until we hit EOF
1375                while (true) {
1376                    String pkg = in.readUTF();
1377                    try {
1378                        // is this package still present?
1379                        mPackageManager.getPackageInfo(pkg, 0);
1380                        // if we get here then yes it is; remember it
1381                        mEverStoredApps.add(pkg);
1382                        temp.writeUTF(pkg);
1383                        if (MORE_DEBUG) Slog.v(TAG, "   + " + pkg);
1384                    } catch (NameNotFoundException e) {
1385                        // nope, this package was uninstalled; don't include it
1386                        if (MORE_DEBUG) Slog.v(TAG, "   - " + pkg);
1387                    }
1388                }
1389            } catch (EOFException e) {
1390                // Once we've rewritten the backup history log, atomically replace the
1391                // old one with the new one then reopen the file for continuing use.
1392                if (!tempProcessedFile.renameTo(mEverStored)) {
1393                    Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
1394                }
1395            } catch (IOException e) {
1396                Slog.e(TAG, "Error in processed file", e);
1397            } finally {
1398                try { if (temp != null) temp.close(); } catch (IOException e) {}
1399                try { if (in != null) in.close(); } catch (IOException e) {}
1400            }
1401        }
1402
1403        synchronized (mQueueLock) {
1404            // Resume the full-data backup queue
1405            mFullBackupQueue = readFullBackupSchedule();
1406        }
1407
1408        // Register for broadcasts about package install, etc., so we can
1409        // update the provider list.
1410        IntentFilter filter = new IntentFilter();
1411        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
1412        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1413        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1414        filter.addDataScheme("package");
1415        mContext.registerReceiver(mBroadcastReceiver, filter);
1416        // Register for events related to sdcard installation.
1417        IntentFilter sdFilter = new IntentFilter();
1418        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
1419        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1420        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
1421    }
1422
1423    private ArrayList<FullBackupEntry> readFullBackupSchedule() {
1424        boolean changed = false;
1425        ArrayList<FullBackupEntry> schedule = null;
1426        List<PackageInfo> apps =
1427                PackageManagerBackupAgent.getStorableApplications(mPackageManager);
1428
1429        if (mFullBackupScheduleFile.exists()) {
1430            FileInputStream fstream = null;
1431            BufferedInputStream bufStream = null;
1432            DataInputStream in = null;
1433            try {
1434                fstream = new FileInputStream(mFullBackupScheduleFile);
1435                bufStream = new BufferedInputStream(fstream);
1436                in = new DataInputStream(bufStream);
1437
1438                int version = in.readInt();
1439                if (version != SCHEDULE_FILE_VERSION) {
1440                    Slog.e(TAG, "Unknown backup schedule version " + version);
1441                    return null;
1442                }
1443
1444                final int N = in.readInt();
1445                schedule = new ArrayList<FullBackupEntry>(N);
1446
1447                // HashSet instead of ArraySet specifically because we want the eventual
1448                // lookups against O(hundreds) of entries to be as fast as possible, and
1449                // we discard the set immediately after the scan so the extra memory
1450                // overhead is transient.
1451                HashSet<String> foundApps = new HashSet<String>(N);
1452
1453                for (int i = 0; i < N; i++) {
1454                    String pkgName = in.readUTF();
1455                    long lastBackup = in.readLong();
1456                    foundApps.add(pkgName); // all apps that we've addressed already
1457                    try {
1458                        PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
1459                        if (appGetsFullBackup(pkg) && appIsEligibleForBackup(pkg.applicationInfo)) {
1460                            schedule.add(new FullBackupEntry(pkgName, lastBackup));
1461                        } else {
1462                            if (DEBUG) {
1463                                Slog.i(TAG, "Package " + pkgName
1464                                        + " no longer eligible for full backup");
1465                            }
1466                        }
1467                    } catch (NameNotFoundException e) {
1468                        if (DEBUG) {
1469                            Slog.i(TAG, "Package " + pkgName
1470                                    + " not installed; dropping from full backup");
1471                        }
1472                    }
1473                }
1474
1475                // New apps can arrive "out of band" via OTA and similar, so we also need to
1476                // scan to make sure that we're tracking all full-backup candidates properly
1477                for (PackageInfo app : apps) {
1478                    if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
1479                        if (!foundApps.contains(app.packageName)) {
1480                            if (MORE_DEBUG) {
1481                                Slog.i(TAG, "New full backup app " + app.packageName + " found");
1482                            }
1483                            schedule.add(new FullBackupEntry(app.packageName, 0));
1484                            changed = true;
1485                        }
1486                    }
1487                }
1488
1489                Collections.sort(schedule);
1490            } catch (Exception e) {
1491                Slog.e(TAG, "Unable to read backup schedule", e);
1492                mFullBackupScheduleFile.delete();
1493                schedule = null;
1494            } finally {
1495                IoUtils.closeQuietly(in);
1496                IoUtils.closeQuietly(bufStream);
1497                IoUtils.closeQuietly(fstream);
1498            }
1499        }
1500
1501        if (schedule == null) {
1502            // no prior queue record, or unable to read it.  Set up the queue
1503            // from scratch.
1504            changed = true;
1505            schedule = new ArrayList<FullBackupEntry>(apps.size());
1506            for (PackageInfo info : apps) {
1507                if (appGetsFullBackup(info) && appIsEligibleForBackup(info.applicationInfo)) {
1508                    schedule.add(new FullBackupEntry(info.packageName, 0));
1509                }
1510            }
1511        }
1512
1513        if (changed) {
1514            writeFullBackupScheduleAsync();
1515        }
1516        return schedule;
1517    }
1518
1519    Runnable mFullBackupScheduleWriter = new Runnable() {
1520        @Override public void run() {
1521            synchronized (mQueueLock) {
1522                try {
1523                    ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
1524                    DataOutputStream bufOut = new DataOutputStream(bufStream);
1525                    bufOut.writeInt(SCHEDULE_FILE_VERSION);
1526
1527                    // version 1:
1528                    //
1529                    // [int] # of packages in the queue = N
1530                    // N * {
1531                    //     [utf8] package name
1532                    //     [long] last backup time for this package
1533                    //     }
1534                    int N = mFullBackupQueue.size();
1535                    bufOut.writeInt(N);
1536
1537                    for (int i = 0; i < N; i++) {
1538                        FullBackupEntry entry = mFullBackupQueue.get(i);
1539                        bufOut.writeUTF(entry.packageName);
1540                        bufOut.writeLong(entry.lastBackup);
1541                    }
1542                    bufOut.flush();
1543
1544                    AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
1545                    FileOutputStream out = af.startWrite();
1546                    out.write(bufStream.toByteArray());
1547                    af.finishWrite(out);
1548                } catch (Exception e) {
1549                    Slog.e(TAG, "Unable to write backup schedule!", e);
1550                }
1551            }
1552        }
1553    };
1554
1555    private void writeFullBackupScheduleAsync() {
1556        mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
1557        mBackupHandler.post(mFullBackupScheduleWriter);
1558    }
1559
1560    private void parseLeftoverJournals() {
1561        for (File f : mJournalDir.listFiles()) {
1562            if (mJournal == null || f.compareTo(mJournal) != 0) {
1563                // This isn't the current journal, so it must be a leftover.  Read
1564                // out the package names mentioned there and schedule them for
1565                // backup.
1566                DataInputStream in = null;
1567                try {
1568                    Slog.i(TAG, "Found stale backup journal, scheduling");
1569                    // Journals will tend to be on the order of a few kilobytes(around 4k), hence,
1570                    // setting the buffer size to 8192.
1571                    InputStream bufferedInputStream = new BufferedInputStream(
1572                            new FileInputStream(f), 8192);
1573                    in = new DataInputStream(bufferedInputStream);
1574                    while (true) {
1575                        String packageName = in.readUTF();
1576                        if (MORE_DEBUG) Slog.i(TAG, "  " + packageName);
1577                        dataChangedImpl(packageName);
1578                    }
1579                } catch (EOFException e) {
1580                    // no more data; we're done
1581                } catch (Exception e) {
1582                    Slog.e(TAG, "Can't read " + f, e);
1583                } finally {
1584                    // close/delete the file
1585                    try { if (in != null) in.close(); } catch (IOException e) {}
1586                    f.delete();
1587                }
1588            }
1589        }
1590    }
1591
1592    private SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) {
1593        return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds);
1594    }
1595
1596    private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) {
1597        try {
1598            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
1599            KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
1600            return keyFactory.generateSecret(ks);
1601        } catch (InvalidKeySpecException e) {
1602            Slog.e(TAG, "Invalid key spec for PBKDF2!");
1603        } catch (NoSuchAlgorithmException e) {
1604            Slog.e(TAG, "PBKDF2 unavailable!");
1605        }
1606        return null;
1607    }
1608
1609    private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) {
1610        SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds);
1611        if (key != null) {
1612            return byteArrayToHex(key.getEncoded());
1613        }
1614        return null;
1615    }
1616
1617    private String byteArrayToHex(byte[] data) {
1618        StringBuilder buf = new StringBuilder(data.length * 2);
1619        for (int i = 0; i < data.length; i++) {
1620            buf.append(Byte.toHexString(data[i], true));
1621        }
1622        return buf.toString();
1623    }
1624
1625    private byte[] hexToByteArray(String digits) {
1626        final int bytes = digits.length() / 2;
1627        if (2*bytes != digits.length()) {
1628            throw new IllegalArgumentException("Hex string must have an even number of digits");
1629        }
1630
1631        byte[] result = new byte[bytes];
1632        for (int i = 0; i < digits.length(); i += 2) {
1633            result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16);
1634        }
1635        return result;
1636    }
1637
1638    private byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) {
1639        char[] mkAsChar = new char[pwBytes.length];
1640        for (int i = 0; i < pwBytes.length; i++) {
1641            mkAsChar[i] = (char) pwBytes[i];
1642        }
1643
1644        Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds);
1645        return checksum.getEncoded();
1646    }
1647
1648    // Used for generating random salts or passwords
1649    private byte[] randomBytes(int bits) {
1650        byte[] array = new byte[bits / 8];
1651        mRng.nextBytes(array);
1652        return array;
1653    }
1654
1655    boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) {
1656        if (mPasswordHash == null) {
1657            // no current password case -- require that 'currentPw' be null or empty
1658            if (candidatePw == null || "".equals(candidatePw)) {
1659                return true;
1660            } // else the non-empty candidate does not match the empty stored pw
1661        } else {
1662            // hash the stated current pw and compare to the stored one
1663            if (candidatePw != null && candidatePw.length() > 0) {
1664                String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds);
1665                if (mPasswordHash.equalsIgnoreCase(currentPwHash)) {
1666                    // candidate hash matches the stored hash -- the password matches
1667                    return true;
1668                }
1669            } // else the stored pw is nonempty but the candidate is empty; no match
1670        }
1671        return false;
1672    }
1673
1674    public boolean setBackupPassword(String currentPw, String newPw) {
1675        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1676                "setBackupPassword");
1677
1678        // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes
1679        final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
1680
1681        // If the supplied pw doesn't hash to the the saved one, fail.  The password
1682        // might be caught in the legacy crypto mismatch; verify that too.
1683        if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
1684                && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
1685                        currentPw, PBKDF2_HASH_ROUNDS))) {
1686            return false;
1687        }
1688
1689        // Snap up to current on the pw file version
1690        mPasswordVersion = BACKUP_PW_FILE_VERSION;
1691        FileOutputStream pwFout = null;
1692        DataOutputStream pwOut = null;
1693        try {
1694            pwFout = new FileOutputStream(mPasswordVersionFile);
1695            pwOut = new DataOutputStream(pwFout);
1696            pwOut.writeInt(mPasswordVersion);
1697        } catch (IOException e) {
1698            Slog.e(TAG, "Unable to write backup pw version; password not changed");
1699            return false;
1700        } finally {
1701            try {
1702                if (pwOut != null) pwOut.close();
1703                if (pwFout != null) pwFout.close();
1704            } catch (IOException e) {
1705                Slog.w(TAG, "Unable to close pw version record");
1706            }
1707        }
1708
1709        // Clearing the password is okay
1710        if (newPw == null || newPw.isEmpty()) {
1711            if (mPasswordHashFile.exists()) {
1712                if (!mPasswordHashFile.delete()) {
1713                    // Unable to delete the old pw file, so fail
1714                    Slog.e(TAG, "Unable to clear backup password");
1715                    return false;
1716                }
1717            }
1718            mPasswordHash = null;
1719            mPasswordSalt = null;
1720            return true;
1721        }
1722
1723        try {
1724            // Okay, build the hash of the new backup password
1725            byte[] salt = randomBytes(PBKDF2_SALT_SIZE);
1726            String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS);
1727
1728            OutputStream pwf = null, buffer = null;
1729            DataOutputStream out = null;
1730            try {
1731                pwf = new FileOutputStream(mPasswordHashFile);
1732                buffer = new BufferedOutputStream(pwf);
1733                out = new DataOutputStream(buffer);
1734                // integer length of the salt array, followed by the salt,
1735                // then the hex pw hash string
1736                out.writeInt(salt.length);
1737                out.write(salt);
1738                out.writeUTF(newPwHash);
1739                out.flush();
1740                mPasswordHash = newPwHash;
1741                mPasswordSalt = salt;
1742                return true;
1743            } finally {
1744                if (out != null) out.close();
1745                if (buffer != null) buffer.close();
1746                if (pwf != null) pwf.close();
1747            }
1748        } catch (IOException e) {
1749            Slog.e(TAG, "Unable to set backup password");
1750        }
1751        return false;
1752    }
1753
1754    public boolean hasBackupPassword() {
1755        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1756                "hasBackupPassword");
1757
1758        return mPasswordHash != null && mPasswordHash.length() > 0;
1759    }
1760
1761    private boolean backupPasswordMatches(String currentPw) {
1762        if (hasBackupPassword()) {
1763            final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
1764            if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
1765                    && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
1766                            currentPw, PBKDF2_HASH_ROUNDS))) {
1767                if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
1768                return false;
1769            }
1770        }
1771        return true;
1772    }
1773
1774    // Maintain persistent state around whether need to do an initialize operation.
1775    // Must be called with the queue lock held.
1776    void recordInitPendingLocked(boolean isPending, String transportName) {
1777        if (MORE_DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
1778                + " on transport " + transportName);
1779        mBackupHandler.removeMessages(MSG_RETRY_INIT);
1780
1781        try {
1782            IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
1783            if (transport != null) {
1784                String transportDirName = transport.transportDirName();
1785                File stateDir = new File(mBaseStateDir, transportDirName);
1786                File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1787
1788                if (isPending) {
1789                    // We need an init before we can proceed with sending backup data.
1790                    // Record that with an entry in our set of pending inits, as well as
1791                    // journaling it via creation of a sentinel file.
1792                    mPendingInits.add(transportName);
1793                    try {
1794                        (new FileOutputStream(initPendingFile)).close();
1795                    } catch (IOException ioe) {
1796                        // Something is badly wrong with our permissions; just try to move on
1797                    }
1798                } else {
1799                    // No more initialization needed; wipe the journal and reset our state.
1800                    initPendingFile.delete();
1801                    mPendingInits.remove(transportName);
1802                }
1803                return; // done; don't fall through to the error case
1804            }
1805        } catch (Exception e) {
1806            // transport threw when asked its name; fall through to the lookup-failed case
1807            Slog.e(TAG, "Transport " + transportName + " failed to report name: "
1808                    + e.getMessage());
1809        }
1810
1811        // The named transport doesn't exist or threw.  This operation is
1812        // important, so we record the need for a an init and post a message
1813        // to retry the init later.
1814        if (isPending) {
1815            mPendingInits.add(transportName);
1816            mBackupHandler.sendMessageDelayed(
1817                    mBackupHandler.obtainMessage(MSG_RETRY_INIT,
1818                            (isPending ? 1 : 0),
1819                            0,
1820                            transportName),
1821                    TRANSPORT_RETRY_INTERVAL);
1822        }
1823    }
1824
1825    // Reset all of our bookkeeping, in response to having been told that
1826    // the backend data has been wiped [due to idle expiry, for example],
1827    // so we must re-upload all saved settings.
1828    void resetBackupState(File stateFileDir) {
1829        synchronized (mQueueLock) {
1830            // Wipe the "what we've ever backed up" tracking
1831            mEverStoredApps.clear();
1832            mEverStored.delete();
1833
1834            mCurrentToken = 0;
1835            writeRestoreTokens();
1836
1837            // Remove all the state files
1838            for (File sf : stateFileDir.listFiles()) {
1839                // ... but don't touch the needs-init sentinel
1840                if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
1841                    sf.delete();
1842                }
1843            }
1844        }
1845
1846        // Enqueue a new backup of every participant
1847        synchronized (mBackupParticipants) {
1848            final int N = mBackupParticipants.size();
1849            for (int i=0; i<N; i++) {
1850                HashSet<String> participants = mBackupParticipants.valueAt(i);
1851                if (participants != null) {
1852                    for (String packageName : participants) {
1853                        dataChangedImpl(packageName);
1854                    }
1855                }
1856            }
1857        }
1858    }
1859
1860    private TransportManager.TransportBoundListener mTransportBoundListener =
1861            new TransportManager.TransportBoundListener() {
1862        @Override
1863        public boolean onTransportBound(IBackupTransport transport) {
1864            // If the init sentinel file exists, we need to be sure to perform the init
1865            // as soon as practical.  We also create the state directory at registration
1866            // time to ensure it's present from the outset.
1867            String name = null;
1868            try {
1869                name = transport.name();
1870                String transportDirName = transport.transportDirName();
1871                File stateDir = new File(mBaseStateDir, transportDirName);
1872                stateDir.mkdirs();
1873
1874                File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1875                if (initSentinel.exists()) {
1876                    synchronized (mQueueLock) {
1877                        mPendingInits.add(name);
1878
1879                        // TODO: pick a better starting time than now + 1 minute
1880                        long delay = 1000 * 60; // one minute, in milliseconds
1881                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1882                                System.currentTimeMillis() + delay, mRunInitIntent);
1883                    }
1884                }
1885                return true;
1886            } catch (Exception e) {
1887                // the transport threw when asked its file naming prefs; declare it invalid
1888                Slog.w(TAG, "Failed to regiser transport: " + name);
1889                return false;
1890            }
1891        }
1892    };
1893
1894    // ----- Track installation/removal of packages -----
1895    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1896        public void onReceive(Context context, Intent intent) {
1897            if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
1898
1899            String action = intent.getAction();
1900            boolean replacing = false;
1901            boolean added = false;
1902            boolean changed = false;
1903            Bundle extras = intent.getExtras();
1904            String pkgList[] = null;
1905            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
1906                    Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
1907                    Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
1908                Uri uri = intent.getData();
1909                if (uri == null) {
1910                    return;
1911                }
1912                String pkgName = uri.getSchemeSpecificPart();
1913                if (pkgName != null) {
1914                    pkgList = new String[] { pkgName };
1915                }
1916                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
1917
1918                // At package-changed we only care about looking at new transport states
1919                if (changed) {
1920                    String[] components =
1921                            intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
1922
1923                    if (MORE_DEBUG) {
1924                        Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
1925                        for (int i = 0; i < components.length; i++) {
1926                            Slog.i(TAG, "   * " + components[i]);
1927                        }
1928                    }
1929
1930                    mTransportManager.onPackageChanged(pkgName, components);
1931                    return; // nothing more to do in the PACKAGE_CHANGED case
1932                }
1933
1934                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1935                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
1936            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
1937                added = true;
1938                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1939            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
1940                added = false;
1941                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1942            }
1943
1944            if (pkgList == null || pkgList.length == 0) {
1945                return;
1946            }
1947
1948            final int uid = extras.getInt(Intent.EXTRA_UID);
1949            if (added) {
1950                synchronized (mBackupParticipants) {
1951                    if (replacing) {
1952                        // This is the package-replaced case; we just remove the entry
1953                        // under the old uid and fall through to re-add.  If an app
1954                        // just added key/value backup participation, this picks it up
1955                        // as a known participant.
1956                        removePackageParticipantsLocked(pkgList, uid);
1957                    }
1958                    addPackageParticipantsLocked(pkgList);
1959                }
1960                // If they're full-backup candidates, add them there instead
1961                final long now = System.currentTimeMillis();
1962                for (String packageName : pkgList) {
1963                    try {
1964                        PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
1965                        if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
1966                            enqueueFullBackup(packageName, now);
1967                            scheduleNextFullBackupJob(0);
1968                        } else {
1969                            // The app might have just transitioned out of full-data into
1970                            // doing key/value backups, or might have just disabled backups
1971                            // entirely.  Make sure it is no longer in the full-data queue.
1972                            synchronized (mQueueLock) {
1973                                dequeueFullBackupLocked(packageName);
1974                            }
1975                            writeFullBackupScheduleAsync();
1976                        }
1977
1978                        mTransportManager.onPackageAdded(packageName);
1979
1980                    } catch (NameNotFoundException e) {
1981                        // doesn't really exist; ignore it
1982                        if (DEBUG) {
1983                            Slog.w(TAG, "Can't resolve new app " + packageName);
1984                        }
1985                    }
1986                }
1987
1988                // Whenever a package is added or updated we need to update
1989                // the package metadata bookkeeping.
1990                dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
1991            } else {
1992                if (replacing) {
1993                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
1994                } else {
1995                    // Outright removal.  In the full-data case, the app will be dropped
1996                    // from the queue when its (now obsolete) name comes up again for
1997                    // backup.
1998                    synchronized (mBackupParticipants) {
1999                        removePackageParticipantsLocked(pkgList, uid);
2000                    }
2001                }
2002                for (String pkgName : pkgList) {
2003                    mTransportManager.onPackageRemoved(pkgName);
2004                }
2005            }
2006        }
2007    };
2008
2009    // Add the backup agents in the given packages to our set of known backup participants.
2010    // If 'packageNames' is null, adds all backup agents in the whole system.
2011    void addPackageParticipantsLocked(String[] packageNames) {
2012        // Look for apps that define the android:backupAgent attribute
2013        List<PackageInfo> targetApps = allAgentPackages();
2014        if (packageNames != null) {
2015            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
2016            for (String packageName : packageNames) {
2017                addPackageParticipantsLockedInner(packageName, targetApps);
2018            }
2019        } else {
2020            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
2021            addPackageParticipantsLockedInner(null, targetApps);
2022        }
2023    }
2024
2025    private void addPackageParticipantsLockedInner(String packageName,
2026            List<PackageInfo> targetPkgs) {
2027        if (MORE_DEBUG) {
2028            Slog.v(TAG, "Examining " + packageName + " for backup agent");
2029        }
2030
2031        for (PackageInfo pkg : targetPkgs) {
2032            if (packageName == null || pkg.packageName.equals(packageName)) {
2033                int uid = pkg.applicationInfo.uid;
2034                HashSet<String> set = mBackupParticipants.get(uid);
2035                if (set == null) {
2036                    set = new HashSet<>();
2037                    mBackupParticipants.put(uid, set);
2038                }
2039                set.add(pkg.packageName);
2040                if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
2041
2042                // Schedule a backup for it on general principles
2043                if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
2044                Message msg = mBackupHandler
2045                        .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
2046                mBackupHandler.sendMessage(msg);
2047            }
2048        }
2049    }
2050
2051    // Remove the given packages' entries from our known active set.
2052    void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
2053        if (packageNames == null) {
2054            Slog.w(TAG, "removePackageParticipants with null list");
2055            return;
2056        }
2057
2058        if (MORE_DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
2059                + " #" + packageNames.length);
2060        for (String pkg : packageNames) {
2061            // Known previous UID, so we know which package set to check
2062            HashSet<String> set = mBackupParticipants.get(oldUid);
2063            if (set != null && set.contains(pkg)) {
2064                removePackageFromSetLocked(set, pkg);
2065                if (set.isEmpty()) {
2066                    if (MORE_DEBUG) Slog.v(TAG, "  last one of this uid; purging set");
2067                    mBackupParticipants.remove(oldUid);
2068                }
2069            }
2070        }
2071    }
2072
2073    private void removePackageFromSetLocked(final HashSet<String> set,
2074            final String packageName) {
2075        if (set.contains(packageName)) {
2076            // Found it.  Remove this one package from the bookkeeping, and
2077            // if it's the last participating app under this uid we drop the
2078            // (now-empty) set as well.
2079            // Note that we deliberately leave it 'known' in the "ever backed up"
2080            // bookkeeping so that its current-dataset data will be retrieved
2081            // if the app is subsequently reinstalled
2082            if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
2083            set.remove(packageName);
2084            mPendingBackups.remove(packageName);
2085        }
2086    }
2087
2088    // Returns the set of all applications that define an android:backupAgent attribute
2089    List<PackageInfo> allAgentPackages() {
2090        // !!! TODO: cache this and regenerate only when necessary
2091        int flags = PackageManager.GET_SIGNATURES;
2092        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
2093        int N = packages.size();
2094        for (int a = N-1; a >= 0; a--) {
2095            PackageInfo pkg = packages.get(a);
2096            try {
2097                ApplicationInfo app = pkg.applicationInfo;
2098                if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
2099                        || app.backupAgentName == null
2100                        || (app.flags&ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0) {
2101                    packages.remove(a);
2102                }
2103                else {
2104                    // we will need the shared library path, so look that up and store it here.
2105                    // This is used implicitly when we pass the PackageInfo object off to
2106                    // the Activity Manager to launch the app for backup/restore purposes.
2107                    app = mPackageManager.getApplicationInfo(pkg.packageName,
2108                            PackageManager.GET_SHARED_LIBRARY_FILES);
2109                    pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
2110                }
2111            } catch (NameNotFoundException e) {
2112                packages.remove(a);
2113            }
2114        }
2115        return packages;
2116    }
2117
2118    // Called from the backup tasks: record that the given app has been successfully
2119    // backed up at least once.  This includes both key/value and full-data backups
2120    // through the transport.
2121    void logBackupComplete(String packageName) {
2122        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
2123
2124        synchronized (mEverStoredApps) {
2125            if (!mEverStoredApps.add(packageName)) return;
2126
2127            RandomAccessFile out = null;
2128            try {
2129                out = new RandomAccessFile(mEverStored, "rws");
2130                out.seek(out.length());
2131                out.writeUTF(packageName);
2132            } catch (IOException e) {
2133                Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
2134            } finally {
2135                try { if (out != null) out.close(); } catch (IOException e) {}
2136            }
2137        }
2138    }
2139
2140    // Remove our awareness of having ever backed up the given package
2141    void removeEverBackedUp(String packageName) {
2142        if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
2143        if (MORE_DEBUG) Slog.v(TAG, "New set:");
2144
2145        synchronized (mEverStoredApps) {
2146            // Rewrite the file and rename to overwrite.  If we reboot in the middle,
2147            // we'll recognize on initialization time that the package no longer
2148            // exists and fix it up then.
2149            File tempKnownFile = new File(mBaseStateDir, "processed.new");
2150            RandomAccessFile known = null;
2151            try {
2152                known = new RandomAccessFile(tempKnownFile, "rws");
2153                mEverStoredApps.remove(packageName);
2154                for (String s : mEverStoredApps) {
2155                    known.writeUTF(s);
2156                    if (MORE_DEBUG) Slog.v(TAG, "    " + s);
2157                }
2158                known.close();
2159                known = null;
2160                if (!tempKnownFile.renameTo(mEverStored)) {
2161                    throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
2162                }
2163            } catch (IOException e) {
2164                // Bad: we couldn't create the new copy.  For safety's sake we
2165                // abandon the whole process and remove all what's-backed-up
2166                // state entirely, meaning we'll force a backup pass for every
2167                // participant on the next boot or [re]install.
2168                Slog.w(TAG, "Error rewriting " + mEverStored, e);
2169                mEverStoredApps.clear();
2170                tempKnownFile.delete();
2171                mEverStored.delete();
2172            } finally {
2173                try { if (known != null) known.close(); } catch (IOException e) {}
2174            }
2175        }
2176    }
2177
2178    // Persistently record the current and ancestral backup tokens as well
2179    // as the set of packages with data [supposedly] available in the
2180    // ancestral dataset.
2181    void writeRestoreTokens() {
2182        try {
2183            RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
2184
2185            // First, the version number of this record, for futureproofing
2186            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
2187
2188            // Write the ancestral and current tokens
2189            af.writeLong(mAncestralToken);
2190            af.writeLong(mCurrentToken);
2191
2192            // Now write the set of ancestral packages
2193            if (mAncestralPackages == null) {
2194                af.writeInt(-1);
2195            } else {
2196                af.writeInt(mAncestralPackages.size());
2197                if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
2198                for (String pkgName : mAncestralPackages) {
2199                    af.writeUTF(pkgName);
2200                    if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
2201                }
2202            }
2203            af.close();
2204        } catch (IOException e) {
2205            Slog.w(TAG, "Unable to write token file:", e);
2206        }
2207    }
2208
2209    // What name is this transport registered under...?
2210    private String getTransportName(IBackupTransport transport) {
2211        if (MORE_DEBUG) {
2212            Slog.v(TAG, "Searching for transport name of " + transport);
2213        }
2214        return mTransportManager.getTransportName(transport);
2215    }
2216
2217    // fire off a backup agent, blocking until it attaches or times out
2218    IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
2219        IBackupAgent agent = null;
2220        synchronized(mAgentConnectLock) {
2221            mConnecting = true;
2222            mConnectedAgent = null;
2223            try {
2224                if (mActivityManager.bindBackupAgent(app.packageName, mode,
2225                        UserHandle.USER_OWNER)) {
2226                    Slog.d(TAG, "awaiting agent for " + app);
2227
2228                    // success; wait for the agent to arrive
2229                    // only wait 10 seconds for the bind to happen
2230                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
2231                    while (mConnecting && mConnectedAgent == null
2232                            && (System.currentTimeMillis() < timeoutMark)) {
2233                        try {
2234                            mAgentConnectLock.wait(5000);
2235                        } catch (InterruptedException e) {
2236                            // just bail
2237                            Slog.w(TAG, "Interrupted: " + e);
2238                            mConnecting = false;
2239                            mConnectedAgent = null;
2240                        }
2241                    }
2242
2243                    // if we timed out with no connect, abort and move on
2244                    if (mConnecting == true) {
2245                        Slog.w(TAG, "Timeout waiting for agent " + app);
2246                        mConnectedAgent = null;
2247                    }
2248                    if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
2249                    agent = mConnectedAgent;
2250                }
2251            } catch (RemoteException e) {
2252                // can't happen - ActivityManager is local
2253            }
2254        }
2255        if (agent == null) {
2256            try {
2257                mActivityManager.clearPendingBackup();
2258            } catch (RemoteException e) {
2259                // can't happen - ActivityManager is local
2260            }
2261        }
2262        return agent;
2263    }
2264
2265    // clear an application's data, blocking until the operation completes or times out
2266    void clearApplicationDataSynchronous(String packageName) {
2267        // Don't wipe packages marked allowClearUserData=false
2268        try {
2269            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
2270            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
2271                if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
2272                        + packageName);
2273                return;
2274            }
2275        } catch (NameNotFoundException e) {
2276            Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
2277            return;
2278        }
2279
2280        ClearDataObserver observer = new ClearDataObserver();
2281
2282        synchronized(mClearDataLock) {
2283            mClearingData = true;
2284            try {
2285                mActivityManager.clearApplicationUserData(packageName, observer, 0);
2286            } catch (RemoteException e) {
2287                // can't happen because the activity manager is in this process
2288            }
2289
2290            // only wait 10 seconds for the clear data to happen
2291            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
2292            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
2293                try {
2294                    mClearDataLock.wait(5000);
2295                } catch (InterruptedException e) {
2296                    // won't happen, but still.
2297                    mClearingData = false;
2298                }
2299            }
2300        }
2301    }
2302
2303    class ClearDataObserver extends IPackageDataObserver.Stub {
2304        public void onRemoveCompleted(String packageName, boolean succeeded) {
2305            synchronized(mClearDataLock) {
2306                mClearingData = false;
2307                mClearDataLock.notifyAll();
2308            }
2309        }
2310    }
2311
2312    // Get the restore-set token for the best-available restore set for this package:
2313    // the active set if possible, else the ancestral one.  Returns zero if none available.
2314    public long getAvailableRestoreToken(String packageName) {
2315        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2316                "getAvailableRestoreToken");
2317
2318        long token = mAncestralToken;
2319        synchronized (mQueueLock) {
2320            if (mEverStoredApps.contains(packageName)) {
2321                if (MORE_DEBUG) {
2322                    Slog.i(TAG, "App in ever-stored, so using current token");
2323                }
2324                token = mCurrentToken;
2325            }
2326        }
2327        if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
2328        return token;
2329    }
2330
2331    public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
2332        return requestBackup(packages, observer, null, flags);
2333    }
2334
2335    public int requestBackup(String[] packages, IBackupObserver observer,
2336            IBackupManagerMonitor monitor, int flags) {
2337        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
2338
2339        if (packages == null || packages.length < 1) {
2340            Slog.e(TAG, "No packages named for backup request");
2341            sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
2342            monitor = monitorEvent(monitor, BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
2343                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT);
2344            throw new IllegalArgumentException("No packages are provided for backup");
2345        }
2346
2347        IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
2348        if (transport == null) {
2349            sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
2350            return BackupManager.ERROR_TRANSPORT_ABORTED;
2351        }
2352
2353        ArrayList<String> fullBackupList = new ArrayList<>();
2354        ArrayList<String> kvBackupList = new ArrayList<>();
2355        for (String packageName : packages) {
2356            if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
2357                kvBackupList.add(packageName);
2358                continue;
2359            }
2360            try {
2361                PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
2362                        PackageManager.GET_SIGNATURES);
2363                if (!appIsEligibleForBackup(packageInfo.applicationInfo)) {
2364                    sendBackupOnPackageResult(observer, packageName,
2365                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
2366                    continue;
2367                }
2368                if (appGetsFullBackup(packageInfo)) {
2369                    fullBackupList.add(packageInfo.packageName);
2370                } else {
2371                    kvBackupList.add(packageInfo.packageName);
2372                }
2373            } catch (NameNotFoundException e) {
2374                sendBackupOnPackageResult(observer, packageName,
2375                        BackupManager.ERROR_PACKAGE_NOT_FOUND);
2376            }
2377        }
2378        EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
2379                fullBackupList.size());
2380        if (MORE_DEBUG) {
2381            Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: " +
2382                fullBackupList.size() + " full backups, " + kvBackupList.size() + " k/v backups");
2383        }
2384
2385        String dirName;
2386        try {
2387            dirName = transport.transportDirName();
2388        } catch (Exception e) {
2389            Slog.e(TAG, "Transport unavailable while attempting backup: " + e.getMessage());
2390            sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
2391            return BackupManager.ERROR_TRANSPORT_ABORTED;
2392        }
2393
2394        boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
2395
2396        Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
2397        msg.obj = new BackupParams(transport, dirName, kvBackupList, fullBackupList, observer,
2398                monitor, true, nonIncrementalBackup);
2399        mBackupHandler.sendMessage(msg);
2400        return BackupManager.SUCCESS;
2401    }
2402
2403    // Cancel all running backups.
2404    public void cancelBackups(){
2405        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
2406        if (MORE_DEBUG) {
2407            Slog.i(TAG, "cancelBackups() called.");
2408        }
2409        final long oldToken = Binder.clearCallingIdentity();
2410        try {
2411            List<Integer> operationsToCancel = new ArrayList<>();
2412            synchronized (mCurrentOpLock) {
2413                for (int i = 0; i < mCurrentOperations.size(); i++) {
2414                    Operation op = mCurrentOperations.valueAt(i);
2415                    int token = mCurrentOperations.keyAt(i);
2416                    if (op.type == OP_TYPE_BACKUP) {
2417                        operationsToCancel.add(token);
2418                    }
2419                }
2420
2421                for (Integer token : operationsToCancel) {
2422                    handleCancel(token, true /* cancelAll */);
2423                }
2424            }
2425
2426            // We don't want the backup jobs to kick in any time soon.
2427            // Reschedules them to run in the distant future.
2428            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS);
2429            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS);
2430        } finally {
2431            Binder.restoreCallingIdentity(oldToken);
2432        }
2433    }
2434
2435    // -----
2436    // Interface and methods used by the asynchronous-with-timeout backup/restore operations
2437
2438    interface BackupRestoreTask {
2439        // Execute one tick of whatever state machine the task implements
2440        void execute();
2441
2442        // An operation that wanted a callback has completed
2443        void operationComplete(long result);
2444
2445        // An operation that wanted a callback has timed out
2446        void handleCancel(boolean cancelAll);
2447    }
2448
2449    void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
2450            int operationType) {
2451        if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
2452            Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation " +
2453                    Integer.toHexString(token) + " of type " + operationType);
2454            return;
2455        }
2456        if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
2457                + " interval=" + interval + " callback=" + callback);
2458
2459        synchronized (mCurrentOpLock) {
2460            mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
2461            Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
2462                    token, 0, callback);
2463            mBackupHandler.sendMessageDelayed(msg, interval);
2464        }
2465    }
2466
2467    private int getMessageIdForOperationType(int operationType) {
2468        switch (operationType) {
2469            case OP_TYPE_BACKUP_WAIT:
2470                return MSG_BACKUP_OPERATION_TIMEOUT;
2471            case OP_TYPE_RESTORE_WAIT:
2472                return MSG_RESTORE_OPERATION_TIMEOUT;
2473            default:
2474                Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: " +
2475                        operationType);
2476                return -1;
2477        }
2478    }
2479
2480    private void removeOperation(int token) {
2481        if (MORE_DEBUG) {
2482            Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
2483        }
2484        synchronized (mCurrentOpLock) {
2485            if (mCurrentOperations.get(token) == null) {
2486                Slog.w(TAG, "Duplicate remove for operation. token=" +
2487                        Integer.toHexString(token));
2488            }
2489            mCurrentOperations.remove(token);
2490        }
2491    }
2492
2493    // synchronous waiter case
2494    boolean waitUntilOperationComplete(int token) {
2495        if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
2496                + Integer.toHexString(token));
2497        int finalState = OP_PENDING;
2498        Operation op = null;
2499        synchronized (mCurrentOpLock) {
2500            while (true) {
2501                op = mCurrentOperations.get(token);
2502                if (op == null) {
2503                    // mysterious disappearance: treat as success with no callback
2504                    break;
2505                } else {
2506                    if (op.state == OP_PENDING) {
2507                        try {
2508                            mCurrentOpLock.wait();
2509                        } catch (InterruptedException e) {
2510                        }
2511                        // When the wait is notified we loop around and recheck the current state
2512                    } else {
2513                        if (MORE_DEBUG) {
2514                            Slog.d(TAG, "Unblocked waiting for operation token=" +
2515                                    Integer.toHexString(token));
2516                        }
2517                        // No longer pending; we're done
2518                        finalState = op.state;
2519                        break;
2520                    }
2521                }
2522            }
2523        }
2524
2525        removeOperation(token);
2526        if (op != null) {
2527            mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
2528        }
2529        if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
2530                + " complete: finalState=" + finalState);
2531        return finalState == OP_ACKNOWLEDGED;
2532    }
2533
2534    void handleCancel(int token, boolean cancelAll) {
2535        // Notify any synchronous waiters
2536        Operation op = null;
2537        synchronized (mCurrentOpLock) {
2538            op = mCurrentOperations.get(token);
2539            if (MORE_DEBUG) {
2540                if (op == null) Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
2541                        + " but no op found");
2542            }
2543            int state = (op != null) ? op.state : OP_TIMEOUT;
2544            if (state == OP_ACKNOWLEDGED) {
2545                // The operation finished cleanly, so we have nothing more to do.
2546                if (DEBUG) {
2547                    Slog.w(TAG, "Operation already got an ack." +
2548                            "Should have been removed from mCurrentOperations.");
2549                }
2550                op = null;
2551                mCurrentOperations.delete(token);
2552            } else if (state == OP_PENDING) {
2553                if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
2554                op.state = OP_TIMEOUT;
2555                // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
2556                // called after we receive cancel here. We need this op's state there.
2557
2558                // Remove all pending timeout messages for this operation type.
2559                mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
2560            }
2561            mCurrentOpLock.notifyAll();
2562        }
2563
2564        // If there's a TimeoutHandler for this event, call it
2565        if (op != null && op.callback != null) {
2566            if (MORE_DEBUG) {
2567                Slog.v(TAG, "   Invoking cancel on " + op.callback);
2568            }
2569            op.callback.handleCancel(cancelAll);
2570        }
2571    }
2572
2573    // ----- Back up a set of applications via a worker thread -----
2574
2575    enum BackupState {
2576        INITIAL,
2577        RUNNING_QUEUE,
2578        FINAL
2579    }
2580
2581    /**
2582     * This class handles the process of backing up a given list of key/value backup packages.
2583     * Also takes in a list of pending dolly backups and kicks them off when key/value backups
2584     * are done.
2585     *
2586     * Flow:
2587     * If required, backup @pm@.
2588     * For each pending key/value backup package:
2589     *     - Bind to agent.
2590     *     - Call agent.doBackup()
2591     *     - Wait either for cancel/timeout or operationComplete() callback from the agent.
2592     * Start task to perform dolly backups.
2593     *
2594     * There are three entry points into this class:
2595     *     - execute() [Called from the handler thread]
2596     *     - operationComplete(long result) [Called from the handler thread]
2597     *     - handleCancel(boolean cancelAll) [Can be called from any thread]
2598     * These methods synchronize on mCancelLock.
2599     *
2600     * Interaction with mCurrentOperations:
2601     *     - An entry for this task is put into mCurrentOperations for the entire lifetime of the
2602     *       task. This is useful to cancel the task if required.
2603     *     - An ephemeral entry is put into mCurrentOperations each time we are waiting on for
2604     *       response from a backup agent. This is used to plumb timeouts and completion callbacks.
2605     */
2606    class PerformBackupTask implements BackupRestoreTask {
2607        private static final String TAG = "PerformBackupTask";
2608
2609        private final Object mCancelLock = new Object();
2610
2611        IBackupTransport mTransport;
2612        ArrayList<BackupRequest> mQueue;
2613        ArrayList<BackupRequest> mOriginalQueue;
2614        File mStateDir;
2615        File mJournal;
2616        BackupState mCurrentState;
2617        ArrayList<String> mPendingFullBackups;
2618        IBackupObserver mObserver;
2619        IBackupManagerMonitor mMonitor;
2620
2621        private final PerformFullTransportBackupTask mFullBackupTask;
2622        private final int mCurrentOpToken;
2623        private volatile int mEphemeralOpToken;
2624
2625        // carried information about the current in-flight operation
2626        IBackupAgent mAgentBinder;
2627        PackageInfo mCurrentPackage;
2628        File mSavedStateName;
2629        File mBackupDataName;
2630        File mNewStateName;
2631        ParcelFileDescriptor mSavedState;
2632        ParcelFileDescriptor mBackupData;
2633        ParcelFileDescriptor mNewState;
2634        int mStatus;
2635        boolean mFinished;
2636        final boolean mUserInitiated;
2637        final boolean mNonIncremental;
2638
2639        private volatile boolean mCancelAll;
2640
2641        public PerformBackupTask(IBackupTransport transport, String dirName,
2642                ArrayList<BackupRequest> queue, File journal, IBackupObserver observer,
2643                IBackupManagerMonitor monitor, ArrayList<String> pendingFullBackups,
2644                boolean userInitiated, boolean nonIncremental) {
2645            mTransport = transport;
2646            mOriginalQueue = queue;
2647            mQueue = new ArrayList<>();
2648            mJournal = journal;
2649            mObserver = observer;
2650            mMonitor = monitor;
2651            mPendingFullBackups = pendingFullBackups;
2652            mUserInitiated = userInitiated;
2653            mNonIncremental = nonIncremental;
2654
2655            mStateDir = new File(mBaseStateDir, dirName);
2656            mCurrentOpToken = generateToken();
2657
2658            mCurrentState = BackupState.INITIAL;
2659            mFinished = false;
2660
2661            CountDownLatch latch = new CountDownLatch(1);
2662            String[] fullBackups =
2663                    mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
2664            mFullBackupTask =
2665                    new PerformFullTransportBackupTask(/*fullBackupRestoreObserver*/ null,
2666                            fullBackups, /*updateSchedule*/ false, /*runningJob*/ null, latch,
2667                            mObserver, mMonitor,mUserInitiated);
2668
2669            registerTask();
2670            addBackupTrace("STATE => INITIAL");
2671        }
2672
2673        /**
2674         * Put this task in the repository of running tasks.
2675         */
2676        private void registerTask() {
2677            synchronized (mCurrentOpLock) {
2678                mCurrentOperations.put(mCurrentOpToken, new Operation(OP_PENDING, this,
2679                        OP_TYPE_BACKUP));
2680            }
2681        }
2682
2683        /**
2684         * Remove this task from repository of running tasks.
2685         */
2686        private void unregisterTask() {
2687            removeOperation(mCurrentOpToken);
2688        }
2689
2690        // Main entry point: perform one chunk of work, updating the state as appropriate
2691        // and reposting the next chunk to the primary backup handler thread.
2692        @Override
2693        @GuardedBy("mCancelLock")
2694        public void execute() {
2695            synchronized (mCancelLock) {
2696                switch (mCurrentState) {
2697                    case INITIAL:
2698                        beginBackup();
2699                        break;
2700
2701                    case RUNNING_QUEUE:
2702                        invokeNextAgent();
2703                        break;
2704
2705                    case FINAL:
2706                        if (!mFinished) finalizeBackup();
2707                        else {
2708                            Slog.e(TAG, "Duplicate finish");
2709                        }
2710                        mFinished = true;
2711                        break;
2712                }
2713            }
2714        }
2715
2716        // We're starting a backup pass.  Initialize the transport and send
2717        // the PM metadata blob if we haven't already.
2718        void beginBackup() {
2719            if (DEBUG_BACKUP_TRACE) {
2720                clearBackupTrace();
2721                StringBuilder b = new StringBuilder(256);
2722                b.append("beginBackup: [");
2723                for (BackupRequest req : mOriginalQueue) {
2724                    b.append(' ');
2725                    b.append(req.packageName);
2726                }
2727                b.append(" ]");
2728                addBackupTrace(b.toString());
2729            }
2730
2731            mAgentBinder = null;
2732            mStatus = BackupTransport.TRANSPORT_OK;
2733
2734            // Sanity check: if the queue is empty we have no work to do.
2735            if (mOriginalQueue.isEmpty() && mPendingFullBackups.isEmpty()) {
2736                Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
2737                addBackupTrace("queue empty at begin");
2738                sendBackupFinished(mObserver, BackupManager.SUCCESS);
2739                executeNextState(BackupState.FINAL);
2740                return;
2741            }
2742
2743            // We need to retain the original queue contents in case of transport
2744            // failure, but we want a working copy that we can manipulate along
2745            // the way.
2746            mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
2747
2748            // When the transport is forcing non-incremental key/value payloads, we send the
2749            // metadata only if it explicitly asks for it.
2750            boolean skipPm = mNonIncremental;
2751
2752            // The app metadata pseudopackage might also be represented in the
2753            // backup queue if apps have been added/removed since the last time
2754            // we performed a backup.  Drop it from the working queue now that
2755            // we're committed to evaluating it for backup regardless.
2756            for (int i = 0; i < mQueue.size(); i++) {
2757                if (PACKAGE_MANAGER_SENTINEL.equals(mQueue.get(i).packageName)) {
2758                    if (MORE_DEBUG) {
2759                        Slog.i(TAG, "Metadata in queue; eliding");
2760                    }
2761                    mQueue.remove(i);
2762                    skipPm = false;
2763                    break;
2764                }
2765            }
2766
2767            if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
2768
2769            File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2770            try {
2771                final String transportName = mTransport.transportDirName();
2772                EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
2773
2774                // If we haven't stored package manager metadata yet, we must init the transport.
2775                if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
2776                    Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
2777                    addBackupTrace("initializing transport " + transportName);
2778                    resetBackupState(mStateDir);  // Just to make sure.
2779                    mStatus = mTransport.initializeDevice();
2780
2781                    addBackupTrace("transport.initializeDevice() == " + mStatus);
2782                    if (mStatus == BackupTransport.TRANSPORT_OK) {
2783                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
2784                    } else {
2785                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
2786                        Slog.e(TAG, "Transport error in initializeDevice()");
2787                    }
2788                }
2789
2790                if (skipPm) {
2791                    Slog.d(TAG, "Skipping backup of package metadata.");
2792                    executeNextState(BackupState.RUNNING_QUEUE);
2793                } else {
2794                    // The package manager doesn't have a proper <application> etc, but since
2795                    // it's running here in the system process we can just set up its agent
2796                    // directly and use a synthetic BackupRequest.  We always run this pass
2797                    // because it's cheap and this way we guarantee that we don't get out of
2798                    // step even if we're selecting among various transports at run time.
2799                    if (mStatus == BackupTransport.TRANSPORT_OK) {
2800                        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
2801                                mPackageManager);
2802                        mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
2803                                IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
2804                        addBackupTrace("PMBA invoke: " + mStatus);
2805
2806                        // Because the PMBA is a local instance, it has already executed its
2807                        // backup callback and returned.  Blow away the lingering (spurious)
2808                        // pending timeout message for it.
2809                        mBackupHandler.removeMessages(MSG_BACKUP_OPERATION_TIMEOUT);
2810                    }
2811                }
2812
2813                if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
2814                    // The backend reports that our dataset has been wiped.  Note this in
2815                    // the event log; the no-success code below will reset the backup
2816                    // state as well.
2817                    EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
2818                }
2819            } catch (Exception e) {
2820                Slog.e(TAG, "Error in backup thread", e);
2821                addBackupTrace("Exception in backup thread: " + e);
2822                mStatus = BackupTransport.TRANSPORT_ERROR;
2823            } finally {
2824                // If we've succeeded so far, invokeAgentForBackup() will have run the PM
2825                // metadata and its completion/timeout callback will continue the state
2826                // machine chain.  If it failed that won't happen; we handle that now.
2827                addBackupTrace("exiting prelim: " + mStatus);
2828                if (mStatus != BackupTransport.TRANSPORT_OK) {
2829                    // if things went wrong at this point, we need to
2830                    // restage everything and try again later.
2831                    resetBackupState(mStateDir);  // Just to make sure.
2832                    // In case of any other error, it's backup transport error.
2833                    sendBackupFinished(mObserver, BackupManager.ERROR_TRANSPORT_ABORTED);
2834                    executeNextState(BackupState.FINAL);
2835                }
2836            }
2837        }
2838
2839        // Transport has been initialized and the PM metadata submitted successfully
2840        // if that was warranted.  Now we process the single next thing in the queue.
2841        void invokeNextAgent() {
2842            mStatus = BackupTransport.TRANSPORT_OK;
2843            addBackupTrace("invoke q=" + mQueue.size());
2844
2845            // Sanity check that we have work to do.  If not, skip to the end where
2846            // we reestablish the wakelock invariants etc.
2847            if (mQueue.isEmpty()) {
2848                if (MORE_DEBUG) Slog.i(TAG, "queue now empty");
2849                executeNextState(BackupState.FINAL);
2850                return;
2851            }
2852
2853            // pop the entry we're going to process on this step
2854            BackupRequest request = mQueue.get(0);
2855            mQueue.remove(0);
2856
2857            Slog.d(TAG, "starting key/value backup of " + request);
2858            addBackupTrace("launch agent for " + request.packageName);
2859
2860            // Verify that the requested app exists; it might be something that
2861            // requested a backup but was then uninstalled.  The request was
2862            // journalled and rather than tamper with the journal it's safer
2863            // to sanity-check here.  This also gives us the classname of the
2864            // package's backup agent.
2865            try {
2866                mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
2867                        PackageManager.GET_SIGNATURES);
2868                if (!appIsEligibleForBackup(mCurrentPackage.applicationInfo)) {
2869                    // The manifest has changed but we had a stale backup request pending.
2870                    // This won't happen again because the app won't be requesting further
2871                    // backups.
2872                    Slog.i(TAG, "Package " + request.packageName
2873                            + " no longer supports backup; skipping");
2874                    addBackupTrace("skipping - not eligible, completion is noop");
2875                    // Shouldn't happen in case of requested backup, as pre-check was done in
2876                    // #requestBackup(), except to app update done concurrently
2877                    sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2878                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
2879                    executeNextState(BackupState.RUNNING_QUEUE);
2880                    return;
2881                }
2882
2883                if (appGetsFullBackup(mCurrentPackage)) {
2884                    // It's possible that this app *formerly* was enqueued for key/value backup,
2885                    // but has since been updated and now only supports the full-data path.
2886                    // Don't proceed with a key/value backup for it in this case.
2887                    Slog.i(TAG, "Package " + request.packageName
2888                            + " requests full-data rather than key/value; skipping");
2889                    addBackupTrace("skipping - fullBackupOnly, completion is noop");
2890                    // Shouldn't happen in case of requested backup, as pre-check was done in
2891                    // #requestBackup()
2892                    sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2893                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
2894                    executeNextState(BackupState.RUNNING_QUEUE);
2895                    return;
2896                }
2897
2898                if (appIsStopped(mCurrentPackage.applicationInfo)) {
2899                    // The app has been force-stopped or cleared or just installed,
2900                    // and not yet launched out of that state, so just as it won't
2901                    // receive broadcasts, we won't run it for backup.
2902                    addBackupTrace("skipping - stopped");
2903                    sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2904                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
2905                    executeNextState(BackupState.RUNNING_QUEUE);
2906                    return;
2907                }
2908
2909                IBackupAgent agent = null;
2910                try {
2911                    mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
2912                    agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
2913                            ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
2914                    addBackupTrace("agent bound; a? = " + (agent != null));
2915                    if (agent != null) {
2916                        mAgentBinder = agent;
2917                        mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
2918                        // at this point we'll either get a completion callback from the
2919                        // agent, or a timeout message on the main handler.  either way, we're
2920                        // done here as long as we're successful so far.
2921                    } else {
2922                        // Timeout waiting for the agent
2923                        mStatus = BackupTransport.AGENT_ERROR;
2924                    }
2925                } catch (SecurityException ex) {
2926                    // Try for the next one.
2927                    Slog.d(TAG, "error in bind/backup", ex);
2928                    mStatus = BackupTransport.AGENT_ERROR;
2929                            addBackupTrace("agent SE");
2930                }
2931            } catch (NameNotFoundException e) {
2932                Slog.d(TAG, "Package does not exist; skipping");
2933                addBackupTrace("no such package");
2934                mStatus = BackupTransport.AGENT_UNKNOWN;
2935            } finally {
2936                mWakelock.setWorkSource(null);
2937
2938                // If there was an agent error, no timeout/completion handling will occur.
2939                // That means we need to direct to the next state ourselves.
2940                if (mStatus != BackupTransport.TRANSPORT_OK) {
2941                    BackupState nextState = BackupState.RUNNING_QUEUE;
2942                    mAgentBinder = null;
2943
2944                    // An agent-level failure means we reenqueue this one agent for
2945                    // a later retry, but otherwise proceed normally.
2946                    if (mStatus == BackupTransport.AGENT_ERROR) {
2947                        if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
2948                                + " - restaging");
2949                        dataChangedImpl(request.packageName);
2950                        mStatus = BackupTransport.TRANSPORT_OK;
2951                        if (mQueue.isEmpty()) nextState = BackupState.FINAL;
2952                        sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2953                                BackupManager.ERROR_AGENT_FAILURE);
2954                    } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
2955                        // Failed lookup of the app, so we couldn't bring up an agent, but
2956                        // we're otherwise fine.  Just drop it and go on to the next as usual.
2957                        mStatus = BackupTransport.TRANSPORT_OK;
2958                        sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2959                                BackupManager.ERROR_PACKAGE_NOT_FOUND);
2960                    } else {
2961                        // Transport-level failure means we reenqueue everything
2962                        revertAndEndBackup();
2963                        nextState = BackupState.FINAL;
2964                    }
2965
2966                    executeNextState(nextState);
2967                } else {
2968                    // success case
2969                    addBackupTrace("expecting completion/timeout callback");
2970                }
2971            }
2972        }
2973
2974        void finalizeBackup() {
2975            addBackupTrace("finishing");
2976
2977            // Mark packages that we didn't backup (because backup was cancelled, etc.) as needing
2978            // backup.
2979            for (BackupRequest req : mQueue) {
2980                dataChangedImpl(req.packageName);
2981            }
2982
2983            // Either backup was successful, in which case we of course do not need
2984            // this pass's journal any more; or it failed, in which case we just
2985            // re-enqueued all of these packages in the current active journal.
2986            // Either way, we no longer need this pass's journal.
2987            if (mJournal != null && !mJournal.delete()) {
2988                Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
2989            }
2990
2991            // If everything actually went through and this is the first time we've
2992            // done a backup, we can now record what the current backup dataset token
2993            // is.
2994            if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) {
2995                addBackupTrace("success; recording token");
2996                try {
2997                    mCurrentToken = mTransport.getCurrentRestoreSet();
2998                    writeRestoreTokens();
2999                } catch (Exception e) {
3000                    // nothing for it at this point, unfortunately, but this will be
3001                    // recorded the next time we fully succeed.
3002                    Slog.e(TAG, "Transport threw reporting restore set: " + e.getMessage());
3003                    addBackupTrace("transport threw returning token");
3004                }
3005            }
3006
3007            // Set up the next backup pass - at this point we can set mBackupRunning
3008            // to false to allow another pass to fire, because we're done with the
3009            // state machine sequence and the wakelock is refcounted.
3010            synchronized (mQueueLock) {
3011                mBackupRunning = false;
3012                if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
3013                    // Make sure we back up everything and perform the one-time init
3014                    if (MORE_DEBUG) Slog.d(TAG, "Server requires init; rerunning");
3015                    addBackupTrace("init required; rerunning");
3016                    try {
3017                        final String name = mTransportManager.getTransportName(mTransport);
3018                        if (name != null) {
3019                            mPendingInits.add(name);
3020                        } else {
3021                            if (DEBUG) {
3022                                Slog.w(TAG, "Couldn't find name of transport " + mTransport
3023                                        + " for init");
3024                            }
3025                        }
3026                    } catch (Exception e) {
3027                        Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage());
3028                        // swallow it and proceed; we don't rely on this
3029                    }
3030                    clearMetadata();
3031                    backupNow();
3032                }
3033            }
3034
3035            clearBackupTrace();
3036
3037            unregisterTask();
3038
3039            if (!mCancelAll && mStatus == BackupTransport.TRANSPORT_OK &&
3040                    mPendingFullBackups != null && !mPendingFullBackups.isEmpty()) {
3041                Slog.d(TAG, "Starting full backups for: " + mPendingFullBackups);
3042                CountDownLatch latch = new CountDownLatch(1);
3043                String[] fullBackups =
3044                        mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
3045                PerformFullTransportBackupTask task =
3046                        new PerformFullTransportBackupTask(/*fullBackupRestoreObserver*/ null,
3047                                fullBackups, /*updateSchedule*/ false, /*runningJob*/ null, latch,
3048                                mObserver, mMonitor, mUserInitiated);
3049                // Acquiring wakelock for PerformFullTransportBackupTask before its start.
3050                mWakelock.acquire();
3051                (new Thread(mFullBackupTask, "full-transport-requested")).start();
3052            } else if (mCancelAll) {
3053                mFullBackupTask.unregisterTask();
3054                sendBackupFinished(mObserver, BackupManager.ERROR_BACKUP_CANCELLED);
3055            } else {
3056                mFullBackupTask.unregisterTask();
3057                switch (mStatus) {
3058                    case BackupTransport.TRANSPORT_OK:
3059                        sendBackupFinished(mObserver, BackupManager.SUCCESS);
3060                        break;
3061                    case BackupTransport.TRANSPORT_NOT_INITIALIZED:
3062                        sendBackupFinished(mObserver, BackupManager.ERROR_TRANSPORT_ABORTED);
3063                        break;
3064                    case BackupTransport.TRANSPORT_ERROR:
3065                    default:
3066                        sendBackupFinished(mObserver, BackupManager.ERROR_TRANSPORT_ABORTED);
3067                        break;
3068                }
3069            }
3070            Slog.i(BackupManagerService.TAG, "K/V backup pass finished.");
3071            // Only once we're entirely finished do we release the wakelock for k/v backup.
3072            mWakelock.release();
3073        }
3074
3075        // Remove the PM metadata state. This will generate an init on the next pass.
3076        void clearMetadata() {
3077            final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
3078            if (pmState.exists()) pmState.delete();
3079        }
3080
3081        // Invoke an agent's doBackup() and start a timeout message spinning on the main
3082        // handler in case it doesn't get back to us.
3083        int invokeAgentForBackup(String packageName, IBackupAgent agent,
3084                IBackupTransport transport) {
3085            if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName);
3086            addBackupTrace("invoking " + packageName);
3087
3088            File blankStateName = new File(mStateDir, "blank_state");
3089            mSavedStateName = new File(mStateDir, packageName);
3090            mBackupDataName = new File(mDataDir, packageName + ".data");
3091            mNewStateName = new File(mStateDir, packageName + ".new");
3092            if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName);
3093
3094            mSavedState = null;
3095            mBackupData = null;
3096            mNewState = null;
3097
3098            boolean callingAgent = false;
3099            mEphemeralOpToken = generateToken();
3100            try {
3101                // Look up the package info & signatures.  This is first so that if it
3102                // throws an exception, there's no file setup yet that would need to
3103                // be unraveled.
3104                if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
3105                    // The metadata 'package' is synthetic; construct one and make
3106                    // sure our global state is pointed at it
3107                    mCurrentPackage = new PackageInfo();
3108                    mCurrentPackage.packageName = packageName;
3109                }
3110
3111                // In a full backup, we pass a null ParcelFileDescriptor as
3112                // the saved-state "file". For key/value backups we pass the old state if
3113                // an incremental backup is required, and a blank state otherwise.
3114                mSavedState = ParcelFileDescriptor.open(
3115                        mNonIncremental ? blankStateName : mSavedStateName,
3116                        ParcelFileDescriptor.MODE_READ_ONLY |
3117                        ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
3118
3119                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
3120                        ParcelFileDescriptor.MODE_READ_WRITE |
3121                        ParcelFileDescriptor.MODE_CREATE |
3122                        ParcelFileDescriptor.MODE_TRUNCATE);
3123
3124                if (!SELinux.restorecon(mBackupDataName)) {
3125                    Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
3126                }
3127
3128                mNewState = ParcelFileDescriptor.open(mNewStateName,
3129                        ParcelFileDescriptor.MODE_READ_WRITE |
3130                        ParcelFileDescriptor.MODE_CREATE |
3131                        ParcelFileDescriptor.MODE_TRUNCATE);
3132
3133                final long quota = mTransport.getBackupQuota(packageName, false /* isFullBackup */);
3134                callingAgent = true;
3135
3136                // Initiate the target's backup pass
3137                addBackupTrace("setting timeout");
3138                prepareOperationTimeout(mEphemeralOpToken, TIMEOUT_BACKUP_INTERVAL, this,
3139                        OP_TYPE_BACKUP_WAIT);
3140                addBackupTrace("calling agent doBackup()");
3141
3142                agent.doBackup(mSavedState, mBackupData, mNewState, quota, mEphemeralOpToken,
3143                        mBackupManagerBinder);
3144            } catch (Exception e) {
3145                Slog.e(TAG, "Error invoking for backup on " + packageName + ". " + e);
3146                addBackupTrace("exception: " + e);
3147                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
3148                        e.toString());
3149                errorCleanup();
3150                return callingAgent ? BackupTransport.AGENT_ERROR
3151                        : BackupTransport.TRANSPORT_ERROR;
3152            } finally {
3153                if (mNonIncremental) {
3154                    blankStateName.delete();
3155                }
3156            }
3157
3158            // At this point the agent is off and running.  The next thing to happen will
3159            // either be a callback from the agent, at which point we'll process its data
3160            // for transport, or a timeout.  Either way the next phase will happen in
3161            // response to the TimeoutHandler interface callbacks.
3162            addBackupTrace("invoke success");
3163            return BackupTransport.TRANSPORT_OK;
3164        }
3165
3166        public void failAgent(IBackupAgent agent, String message) {
3167            try {
3168                agent.fail(message);
3169            } catch (Exception e) {
3170                Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName);
3171            }
3172        }
3173
3174        // SHA-1 a byte array and return the result in hex
3175        private String SHA1Checksum(byte[] input) {
3176            final byte[] checksum;
3177            try {
3178                MessageDigest md = MessageDigest.getInstance("SHA-1");
3179                checksum = md.digest(input);
3180            } catch (NoSuchAlgorithmException e) {
3181                Slog.e(TAG, "Unable to use SHA-1!");
3182                return "00";
3183            }
3184
3185            StringBuffer sb = new StringBuffer(checksum.length * 2);
3186            for (int i = 0; i < checksum.length; i++) {
3187                sb.append(Integer.toHexString(checksum[i]));
3188            }
3189            return sb.toString();
3190        }
3191
3192        private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
3193                throws IOException {
3194            // TODO: http://b/22388012
3195            byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
3196                    UserHandle.USER_SYSTEM);
3197            // has the widget state changed since last time?
3198            final File widgetFile = new File(mStateDir, pkgName + "_widget");
3199            final boolean priorStateExists = widgetFile.exists();
3200
3201            if (MORE_DEBUG) {
3202                if (priorStateExists || widgetState != null) {
3203                    Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
3204                            + " prior=" + priorStateExists);
3205                }
3206            }
3207
3208            if (!priorStateExists && widgetState == null) {
3209                // no prior state, no new state => nothing to do
3210                return;
3211            }
3212
3213            // if the new state is not null, we might need to compare checksums to
3214            // determine whether to update the widget blob in the archive.  If the
3215            // widget state *is* null, we know a priori at this point that we simply
3216            // need to commit a deletion for it.
3217            String newChecksum = null;
3218            if (widgetState != null) {
3219                newChecksum = SHA1Checksum(widgetState);
3220                if (priorStateExists) {
3221                    final String priorChecksum;
3222                    try (
3223                        FileInputStream fin = new FileInputStream(widgetFile);
3224                        DataInputStream in = new DataInputStream(fin)
3225                    ) {
3226                        priorChecksum = in.readUTF();
3227                    }
3228                    if (Objects.equals(newChecksum, priorChecksum)) {
3229                        // Same checksum => no state change => don't rewrite the widget data
3230                        return;
3231                    }
3232                }
3233            } // else widget state *became* empty, so we need to commit a deletion
3234
3235            BackupDataOutput out = new BackupDataOutput(fd);
3236            if (widgetState != null) {
3237                try (
3238                    FileOutputStream fout = new FileOutputStream(widgetFile);
3239                    DataOutputStream stateOut = new DataOutputStream(fout)
3240                ) {
3241                    stateOut.writeUTF(newChecksum);
3242                }
3243
3244                out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
3245                out.writeEntityData(widgetState, widgetState.length);
3246            } else {
3247                // Widget state for this app has been removed; commit a deletion
3248                out.writeEntityHeader(KEY_WIDGET_STATE, -1);
3249                widgetFile.delete();
3250            }
3251        }
3252
3253        @Override
3254        @GuardedBy("mCancelLock")
3255        public void operationComplete(long unusedResult) {
3256            removeOperation(mEphemeralOpToken);
3257            synchronized (mCancelLock) {
3258                // The agent reported back to us!
3259                if (mFinished) {
3260                    Slog.d(TAG, "operationComplete received after task finished.");
3261                    return;
3262                }
3263
3264                if (mBackupData == null) {
3265                    // This callback was racing with our timeout, so we've cleaned up the
3266                    // agent state already and are on to the next thing.  We have nothing
3267                    // further to do here: agent state having been cleared means that we've
3268                    // initiated the appropriate next operation.
3269                    final String pkg = (mCurrentPackage != null)
3270                            ? mCurrentPackage.packageName : "[none]";
3271                    if (MORE_DEBUG) {
3272                        Slog.i(TAG, "Callback after agent teardown: " + pkg);
3273                    }
3274                    addBackupTrace("late opComplete; curPkg = " + pkg);
3275                    return;
3276                }
3277
3278                final String pkgName = mCurrentPackage.packageName;
3279                final long filepos = mBackupDataName.length();
3280                FileDescriptor fd = mBackupData.getFileDescriptor();
3281                try {
3282                    // If it's a 3rd party app, see whether they wrote any protected keys
3283                    // and complain mightily if they are attempting shenanigans.
3284                    if (mCurrentPackage.applicationInfo != null &&
3285                            (mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
3286                                    == 0) {
3287                        ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
3288                                ParcelFileDescriptor.MODE_READ_ONLY);
3289                        BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
3290                        try {
3291                            while (in.readNextHeader()) {
3292                                final String key = in.getKey();
3293                                if (key != null && key.charAt(0) >= 0xff00) {
3294                                    // Not okay: crash them and bail.
3295                                    failAgent(mAgentBinder, "Illegal backup key: " + key);
3296                                    addBackupTrace("illegal key " + key + " from " + pkgName);
3297                                    EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
3298                                            "bad key");
3299                                    mBackupHandler.removeMessages(MSG_BACKUP_OPERATION_TIMEOUT);
3300                                    sendBackupOnPackageResult(mObserver, pkgName,
3301                                            BackupManager.ERROR_AGENT_FAILURE);
3302                                    errorCleanup();
3303                                    // agentErrorCleanup() implicitly executes next state properly
3304                                    return;
3305                                }
3306                                in.skipEntityData();
3307                            }
3308                        } finally {
3309                            if (readFd != null) {
3310                                readFd.close();
3311                            }
3312                        }
3313                    }
3314
3315                    // Piggyback the widget state payload, if any
3316                    writeWidgetPayloadIfAppropriate(fd, pkgName);
3317                } catch (IOException e) {
3318                    // Hard disk error; recovery/failure policy TBD.  For now roll back,
3319                    // but we may want to consider this a transport-level failure (i.e.
3320                    // we're in such a bad state that we can't contemplate doing backup
3321                    // operations any more during this pass).
3322                    Slog.w(TAG, "Unable to save widget state for " + pkgName);
3323                    try {
3324                        Os.ftruncate(fd, filepos);
3325                    } catch (ErrnoException ee) {
3326                        Slog.w(TAG, "Unable to roll back!");
3327                    }
3328                }
3329
3330                // Spin the data off to the transport and proceed with the next stage.
3331                if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
3332                        + pkgName);
3333                mBackupHandler.removeMessages(MSG_BACKUP_OPERATION_TIMEOUT);
3334                clearAgentState();
3335                addBackupTrace("operation complete");
3336
3337                ParcelFileDescriptor backupData = null;
3338                mStatus = BackupTransport.TRANSPORT_OK;
3339                long size = 0;
3340                try {
3341                    size = mBackupDataName.length();
3342                    if (size > 0) {
3343                        if (mStatus == BackupTransport.TRANSPORT_OK) {
3344                            backupData = ParcelFileDescriptor.open(mBackupDataName,
3345                                    ParcelFileDescriptor.MODE_READ_ONLY);
3346                            addBackupTrace("sending data to transport");
3347                            int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
3348                            mStatus = mTransport.performBackup(mCurrentPackage, backupData, flags);
3349                        }
3350
3351                        // TODO - We call finishBackup() for each application backed up, because
3352                        // we need to know now whether it succeeded or failed.  Instead, we should
3353                        // hold off on finishBackup() until the end, which implies holding off on
3354                        // renaming *all* the output state files (see below) until that happens.
3355
3356                        addBackupTrace("data delivered: " + mStatus);
3357                        if (mStatus == BackupTransport.TRANSPORT_OK) {
3358                            addBackupTrace("finishing op on transport");
3359                            mStatus = mTransport.finishBackup();
3360                            addBackupTrace("finished: " + mStatus);
3361                        } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3362                            addBackupTrace("transport rejected package");
3363                        }
3364                    } else {
3365                        if (MORE_DEBUG) Slog.i(TAG,
3366                                "no backup data written; not calling transport");
3367                        addBackupTrace("no data to send");
3368                    }
3369
3370                    if (mStatus == BackupTransport.TRANSPORT_OK) {
3371                        // After successful transport, delete the now-stale data
3372                        // and juggle the files so that next time we supply the agent
3373                        // with the new state file it just created.
3374                        mBackupDataName.delete();
3375                        mNewStateName.renameTo(mSavedStateName);
3376                        sendBackupOnPackageResult(mObserver, pkgName, BackupManager.SUCCESS);
3377                        EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
3378                        logBackupComplete(pkgName);
3379                    } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3380                        // The transport has rejected backup of this specific package.  Roll it
3381                        // back but proceed with running the rest of the queue.
3382                        mBackupDataName.delete();
3383                        mNewStateName.delete();
3384                        sendBackupOnPackageResult(mObserver, pkgName,
3385                                BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
3386                        EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
3387                    } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
3388                        sendBackupOnPackageResult(mObserver, pkgName,
3389                                BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
3390                        EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName);
3391                    } else {
3392                        // Actual transport-level failure to communicate the data to the backend
3393                        sendBackupOnPackageResult(mObserver, pkgName,
3394                                BackupManager.ERROR_TRANSPORT_ABORTED);
3395                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
3396                    }
3397                } catch (Exception e) {
3398                    sendBackupOnPackageResult(mObserver, pkgName,
3399                            BackupManager.ERROR_TRANSPORT_ABORTED);
3400                    Slog.e(TAG, "Transport error backing up " + pkgName, e);
3401                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
3402                    mStatus = BackupTransport.TRANSPORT_ERROR;
3403                } finally {
3404                    try {
3405                        if (backupData != null) backupData.close();
3406                    } catch (IOException e) {
3407                    }
3408                }
3409
3410                final BackupState nextState;
3411                if (mStatus == BackupTransport.TRANSPORT_OK
3412                        || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3413                    // Success or single-package rejection.  Proceed with the next app if any,
3414                    // otherwise we're done.
3415                    nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
3416                } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
3417                    if (MORE_DEBUG) {
3418                        Slog.d(TAG, "Package " + mCurrentPackage.packageName +
3419                                " hit quota limit on k/v backup");
3420                    }
3421                    if (mAgentBinder != null) {
3422                        try {
3423                            long quota = mTransport.getBackupQuota(mCurrentPackage.packageName,
3424                                    false);
3425                            mAgentBinder.doQuotaExceeded(size, quota);
3426                        } catch (Exception e) {
3427                            Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage());
3428                        }
3429                    }
3430                    nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
3431                } else {
3432                    // Any other error here indicates a transport-level failure.  That means
3433                    // we need to halt everything and reschedule everything for next time.
3434                    revertAndEndBackup();
3435                    nextState = BackupState.FINAL;
3436                }
3437
3438                executeNextState(nextState);
3439            }
3440        }
3441
3442
3443        @Override
3444        @GuardedBy("mCancelLock")
3445        public void handleCancel(boolean cancelAll) {
3446            removeOperation(mEphemeralOpToken);
3447            synchronized (mCancelLock) {
3448                if (mFinished) {
3449                    // We have already cancelled this operation.
3450                    if (MORE_DEBUG) {
3451                        Slog.d(TAG, "Ignoring stale cancel. cancelAll=" + cancelAll);
3452                    }
3453                    return;
3454                }
3455                mCancelAll = cancelAll;
3456                // Whoops, the current agent timed out running doBackup().  Tidy up and restage
3457                // it for the next time we run a backup pass.
3458                // !!! TODO: keep track of failure counts per agent, and blacklist those which
3459                // fail repeatedly (i.e. have proved themselves to be buggy).
3460                Slog.e(TAG, "Cancel backing up " + mCurrentPackage.packageName);
3461                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName);
3462                addBackupTrace(
3463                        "cancel of " + mCurrentPackage.packageName + ", cancelAll=" + cancelAll);
3464                errorCleanup();
3465                if (!cancelAll) {
3466                    executeNextState(
3467                            mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
3468                    dataChangedImpl(mCurrentPackage.packageName);
3469                } else {
3470                    finalizeBackup();
3471                }
3472            }
3473        }
3474
3475        void revertAndEndBackup() {
3476            if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
3477            addBackupTrace("transport error; reverting");
3478
3479            // We want to reset the backup schedule based on whatever the transport suggests
3480            // by way of retry/backoff time.
3481            long delay;
3482            try {
3483                delay = mTransport.requestBackupTime();
3484            } catch (Exception e) {
3485                Slog.w(TAG, "Unable to contact transport for recommended backoff: " + e.getMessage());
3486                delay = 0;  // use the scheduler's default
3487            }
3488            KeyValueBackupJob.schedule(mContext, delay);
3489
3490            for (BackupRequest request : mOriginalQueue) {
3491                dataChangedImpl(request.packageName);
3492            }
3493
3494        }
3495
3496        void errorCleanup() {
3497            mBackupDataName.delete();
3498            mNewStateName.delete();
3499            clearAgentState();
3500        }
3501
3502        // Cleanup common to both success and failure cases
3503        void clearAgentState() {
3504            try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {}
3505            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
3506            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
3507            synchronized (mCurrentOpLock) {
3508                // Current-operation callback handling requires the validity of these various
3509                // bits of internal state as an invariant of the operation still being live.
3510                // This means we make sure to clear all of the state in unison inside the lock.
3511                mCurrentOperations.remove(mEphemeralOpToken);
3512                mSavedState = mBackupData = mNewState = null;
3513            }
3514
3515            // If this was a pseudopackage there's no associated Activity Manager state
3516            if (mCurrentPackage.applicationInfo != null) {
3517                addBackupTrace("unbinding " + mCurrentPackage.packageName);
3518                try {  // unbind even on timeout, just in case
3519                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
3520                } catch (RemoteException e) { /* can't happen; activity manager is local */ }
3521            }
3522        }
3523
3524        void executeNextState(BackupState nextState) {
3525            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
3526                    + this + " nextState=" + nextState);
3527            addBackupTrace("executeNextState => " + nextState);
3528            mCurrentState = nextState;
3529            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
3530            mBackupHandler.sendMessage(msg);
3531        }
3532    }
3533
3534
3535    // ----- Full backup/restore to a file/socket -----
3536
3537    class FullBackupObbConnection implements ServiceConnection {
3538        volatile IObbBackupService mService;
3539
3540        FullBackupObbConnection() {
3541            mService = null;
3542        }
3543
3544        public void establish() {
3545            if (MORE_DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this);
3546            Intent obbIntent = new Intent().setComponent(new ComponentName(
3547                    "com.android.sharedstoragebackup",
3548                    "com.android.sharedstoragebackup.ObbBackupService"));
3549            BackupManagerService.this.mContext.bindServiceAsUser(
3550                    obbIntent, this, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
3551        }
3552
3553        public void tearDown() {
3554            BackupManagerService.this.mContext.unbindService(this);
3555        }
3556
3557        public boolean backupObbs(PackageInfo pkg, OutputStream out) {
3558            boolean success = false;
3559            waitForConnection();
3560
3561            ParcelFileDescriptor[] pipes = null;
3562            try {
3563                pipes = ParcelFileDescriptor.createPipe();
3564                int token = generateToken();
3565                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL,
3566                        null, OP_TYPE_BACKUP_WAIT);
3567                mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
3568                routeSocketDataToOutput(pipes[0], out);
3569                success = waitUntilOperationComplete(token);
3570            } catch (Exception e) {
3571                Slog.w(TAG, "Unable to back up OBBs for " + pkg, e);
3572            } finally {
3573                try {
3574                    out.flush();
3575                    if (pipes != null) {
3576                        if (pipes[0] != null) pipes[0].close();
3577                        if (pipes[1] != null) pipes[1].close();
3578                    }
3579                } catch (IOException e) {
3580                    Slog.w(TAG, "I/O error closing down OBB backup", e);
3581                }
3582            }
3583            return success;
3584        }
3585
3586        public void restoreObbFile(String pkgName, ParcelFileDescriptor data,
3587                long fileSize, int type, String path, long mode, long mtime,
3588                int token, IBackupManager callbackBinder) {
3589            waitForConnection();
3590
3591            try {
3592                mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime,
3593                        token, callbackBinder);
3594            } catch (Exception e) {
3595                Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e);
3596            }
3597        }
3598
3599        private void waitForConnection() {
3600            synchronized (this) {
3601                while (mService == null) {
3602                    if (MORE_DEBUG) Slog.i(TAG, "...waiting for OBB service binding...");
3603                    try {
3604                        this.wait();
3605                    } catch (InterruptedException e) { /* never interrupted */ }
3606                }
3607                if (MORE_DEBUG) Slog.i(TAG, "Connected to OBB service; continuing");
3608            }
3609        }
3610
3611        @Override
3612        public void onServiceConnected(ComponentName name, IBinder service) {
3613            synchronized (this) {
3614                mService = IObbBackupService.Stub.asInterface(service);
3615                if (MORE_DEBUG) Slog.i(TAG, "OBB service connection " + mService
3616                        + " connected on " + this);
3617                this.notifyAll();
3618            }
3619        }
3620
3621        @Override
3622        public void onServiceDisconnected(ComponentName name) {
3623            synchronized (this) {
3624                mService = null;
3625                if (MORE_DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this);
3626                this.notifyAll();
3627            }
3628        }
3629
3630    }
3631
3632    private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
3633            throws IOException {
3634        // We do not take close() responsibility for the pipe FD
3635        FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
3636        DataInputStream in = new DataInputStream(raw);
3637
3638        byte[] buffer = new byte[32 * 1024];
3639        int chunkTotal;
3640        while ((chunkTotal = in.readInt()) > 0) {
3641            while (chunkTotal > 0) {
3642                int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
3643                int nRead = in.read(buffer, 0, toRead);
3644                out.write(buffer, 0, nRead);
3645                chunkTotal -= nRead;
3646            }
3647        }
3648    }
3649
3650    void tearDownAgentAndKill(ApplicationInfo app) {
3651        if (app == null) {
3652            // Null means the system package, so just quietly move on.  :)
3653            return;
3654        }
3655
3656        try {
3657            // unbind and tidy up even on timeout or failure, just in case
3658            mActivityManager.unbindBackupAgent(app);
3659
3660            // The agent was running with a stub Application object, so shut it down.
3661            // !!! We hardcode the confirmation UI's package name here rather than use a
3662            //     manifest flag!  TODO something less direct.
3663            if (app.uid >= Process.FIRST_APPLICATION_UID
3664                    && !app.packageName.equals("com.android.backupconfirm")) {
3665                if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
3666                mActivityManager.killApplicationProcess(app.processName, app.uid);
3667            } else {
3668                if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
3669            }
3670        } catch (RemoteException e) {
3671            Slog.d(TAG, "Lost app trying to shut down");
3672        }
3673    }
3674
3675    // Core logic for performing one package's full backup, gathering the tarball from the
3676    // application and emitting it to the designated OutputStream.
3677
3678    // Callout from the engine to an interested participant that might need to communicate
3679    // with the agent prior to asking it to move data
3680    interface FullBackupPreflight {
3681        /**
3682         * Perform the preflight operation necessary for the given package.
3683         * @param pkg The name of the package being proposed for full-data backup
3684         * @param agent Live BackupAgent binding to the target app's agent
3685         * @return BackupTransport.TRANSPORT_OK to proceed with the backup operation,
3686         *         or one of the other BackupTransport.* error codes as appropriate
3687         */
3688        int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
3689
3690        long getExpectedSizeOrErrorCode();
3691    };
3692
3693    class FullBackupEngine {
3694        OutputStream mOutput;
3695        FullBackupPreflight mPreflightHook;
3696        BackupRestoreTask mTimeoutMonitor;
3697        IBackupAgent mAgent;
3698        File mFilesDir;
3699        File mManifestFile;
3700        File mMetadataFile;
3701        boolean mIncludeApks;
3702        PackageInfo mPkg;
3703        private final long mQuota;
3704        private final int mOpToken;
3705
3706        class FullBackupRunner implements Runnable {
3707            PackageInfo mPackage;
3708            byte[] mWidgetData;
3709            IBackupAgent mAgent;
3710            ParcelFileDescriptor mPipe;
3711            int mToken;
3712            boolean mSendApk;
3713            boolean mWriteManifest;
3714
3715            FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
3716                             int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
3717                    throws IOException {
3718                mPackage = pack;
3719                mWidgetData = widgetData;
3720                mAgent = agent;
3721                mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
3722                mToken = token;
3723                mSendApk = sendApk;
3724                mWriteManifest = writeManifest;
3725            }
3726
3727            @Override
3728            public void run() {
3729                try {
3730                    FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
3731
3732                    if (mWriteManifest) {
3733                        final boolean writeWidgetData = mWidgetData != null;
3734                        if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
3735                        writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
3736                        FullBackup.backupToTar(mPackage.packageName, null, null,
3737                                mFilesDir.getAbsolutePath(),
3738                                mManifestFile.getAbsolutePath(),
3739                                output);
3740                        mManifestFile.delete();
3741
3742                        // We only need to write a metadata file if we have widget data to stash
3743                        if (writeWidgetData) {
3744                            writeMetadata(mPackage, mMetadataFile, mWidgetData);
3745                            FullBackup.backupToTar(mPackage.packageName, null, null,
3746                                    mFilesDir.getAbsolutePath(),
3747                                    mMetadataFile.getAbsolutePath(),
3748                                    output);
3749                            mMetadataFile.delete();
3750                        }
3751                    }
3752
3753                    if (mSendApk) {
3754                        writeApkToBackup(mPackage, output);
3755                    }
3756
3757                    if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
3758                    prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL,
3759                            mTimeoutMonitor /* in parent class */, OP_TYPE_BACKUP_WAIT);
3760                    mAgent.doFullBackup(mPipe, mQuota, mToken, mBackupManagerBinder);
3761                } catch (IOException e) {
3762                    Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
3763                } catch (RemoteException e) {
3764                    Slog.e(TAG, "Remote agent vanished during full backup of "
3765                            + mPackage.packageName);
3766                } finally {
3767                    try {
3768                        mPipe.close();
3769                    } catch (IOException e) {}
3770                }
3771            }
3772        }
3773
3774        FullBackupEngine(OutputStream output, FullBackupPreflight preflightHook, PackageInfo pkg,
3775                         boolean alsoApks, BackupRestoreTask timeoutMonitor, long quota, int opToken) {
3776            mOutput = output;
3777            mPreflightHook = preflightHook;
3778            mPkg = pkg;
3779            mIncludeApks = alsoApks;
3780            mTimeoutMonitor = timeoutMonitor;
3781            mFilesDir = new File("/data/system");
3782            mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
3783            mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
3784            mQuota = quota;
3785            mOpToken = opToken;
3786        }
3787
3788        public int preflightCheck() throws RemoteException {
3789            if (mPreflightHook == null) {
3790                if (MORE_DEBUG) {
3791                    Slog.v(TAG, "No preflight check");
3792                }
3793                return BackupTransport.TRANSPORT_OK;
3794            }
3795            if (initializeAgent()) {
3796                int result = mPreflightHook.preflightFullBackup(mPkg, mAgent);
3797                if (MORE_DEBUG) {
3798                    Slog.v(TAG, "preflight returned " + result);
3799                }
3800                return result;
3801            } else {
3802                Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
3803                return BackupTransport.AGENT_ERROR;
3804            }
3805        }
3806
3807        public int backupOnePackage() throws RemoteException {
3808            int result = BackupTransport.AGENT_ERROR;
3809
3810            if (initializeAgent()) {
3811                ParcelFileDescriptor[] pipes = null;
3812                try {
3813                    pipes = ParcelFileDescriptor.createPipe();
3814
3815                    ApplicationInfo app = mPkg.applicationInfo;
3816                    final boolean isSharedStorage =
3817                            mPkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
3818                    final boolean sendApk = mIncludeApks
3819                            && !isSharedStorage
3820                            && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
3821                            && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
3822                            (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
3823
3824                    // TODO: http://b/22388012
3825                    byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(mPkg.packageName,
3826                            UserHandle.USER_SYSTEM);
3827
3828                    FullBackupRunner runner = new FullBackupRunner(mPkg, mAgent, pipes[1],
3829                            mOpToken, sendApk, !isSharedStorage, widgetBlob);
3830                    pipes[1].close();   // the runner has dup'd it
3831                    pipes[1] = null;
3832                    Thread t = new Thread(runner, "app-data-runner");
3833                    t.start();
3834
3835                    // Now pull data from the app and stuff it into the output
3836                    routeSocketDataToOutput(pipes[0], mOutput);
3837
3838                    if (!waitUntilOperationComplete(mOpToken)) {
3839                        Slog.e(TAG, "Full backup failed on package " + mPkg.packageName);
3840                    } else {
3841                        if (MORE_DEBUG) {
3842                            Slog.d(TAG, "Full package backup success: " + mPkg.packageName);
3843                        }
3844                        result = BackupTransport.TRANSPORT_OK;
3845                    }
3846                } catch (IOException e) {
3847                    Slog.e(TAG, "Error backing up " + mPkg.packageName + ": " + e.getMessage());
3848                    result = BackupTransport.AGENT_ERROR;
3849                } finally {
3850                    try {
3851                        // flush after every package
3852                        mOutput.flush();
3853                        if (pipes != null) {
3854                            if (pipes[0] != null) pipes[0].close();
3855                            if (pipes[1] != null) pipes[1].close();
3856                        }
3857                    } catch (IOException e) {
3858                        Slog.w(TAG, "Error bringing down backup stack");
3859                        result = BackupTransport.TRANSPORT_ERROR;
3860                    }
3861                }
3862            } else {
3863                Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
3864            }
3865            tearDown();
3866            return result;
3867        }
3868
3869        public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
3870            if (initializeAgent()) {
3871                try {
3872                    mAgent.doQuotaExceeded(backupDataBytes, quotaBytes);
3873                } catch (RemoteException e) {
3874                    Slog.e(TAG, "Remote exception while telling agent about quota exceeded");
3875                }
3876            }
3877        }
3878
3879        private boolean initializeAgent() {
3880            if (mAgent == null) {
3881                if (MORE_DEBUG) {
3882                    Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName);
3883                }
3884                mAgent = bindToAgentSynchronous(mPkg.applicationInfo,
3885                        ApplicationThreadConstants.BACKUP_MODE_FULL);
3886            }
3887            return mAgent != null;
3888        }
3889
3890        private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
3891            // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
3892            // TODO: handle backing up split APKs
3893            final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
3894            final String apkDir = new File(appSourceDir).getParent();
3895            FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
3896                    apkDir, appSourceDir, output);
3897
3898            // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM
3899            // doesn't have access to external storage.
3900
3901            // Save associated .obb content if it exists and we did save the apk
3902            // check for .obb and save those too
3903            // TODO: http://b/22388012
3904            final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_SYSTEM);
3905            final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0];
3906            if (obbDir != null) {
3907                if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
3908                File[] obbFiles = obbDir.listFiles();
3909                if (obbFiles != null) {
3910                    final String obbDirName = obbDir.getAbsolutePath();
3911                    for (File obb : obbFiles) {
3912                        FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
3913                                obbDirName, obb.getAbsolutePath(), output);
3914                    }
3915                }
3916            }
3917        }
3918
3919        private void writeAppManifest(PackageInfo pkg, File manifestFile,
3920                boolean withApk, boolean withWidgets) throws IOException {
3921            // Manifest format. All data are strings ending in LF:
3922            //     BACKUP_MANIFEST_VERSION, currently 1
3923            //
3924            // Version 1:
3925            //     package name
3926            //     package's versionCode
3927            //     platform versionCode
3928            //     getInstallerPackageName() for this package (maybe empty)
3929            //     boolean: "1" if archive includes .apk; any other string means not
3930            //     number of signatures == N
3931            // N*:    signature byte array in ascii format per Signature.toCharsString()
3932            StringBuilder builder = new StringBuilder(4096);
3933            StringBuilderPrinter printer = new StringBuilderPrinter(builder);
3934
3935            printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
3936            printer.println(pkg.packageName);
3937            printer.println(Integer.toString(pkg.versionCode));
3938            printer.println(Integer.toString(Build.VERSION.SDK_INT));
3939
3940            String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
3941            printer.println((installerName != null) ? installerName : "");
3942
3943            printer.println(withApk ? "1" : "0");
3944            if (pkg.signatures == null) {
3945                printer.println("0");
3946            } else {
3947                printer.println(Integer.toString(pkg.signatures.length));
3948                for (Signature sig : pkg.signatures) {
3949                    printer.println(sig.toCharsString());
3950                }
3951            }
3952
3953            FileOutputStream outstream = new FileOutputStream(manifestFile);
3954            outstream.write(builder.toString().getBytes());
3955            outstream.close();
3956
3957            // We want the manifest block in the archive stream to be idempotent:
3958            // each time we generate a backup stream for the app, we want the manifest
3959            // block to be identical.  The underlying tar mechanism sees it as a file,
3960            // though, and will propagate its mtime, causing the tar header to vary.
3961            // Avoid this problem by pinning the mtime to zero.
3962            manifestFile.setLastModified(0);
3963        }
3964
3965        // Widget metadata format. All header entries are strings ending in LF:
3966        //
3967        // Version 1 header:
3968        //     BACKUP_METADATA_VERSION, currently "1"
3969        //     package name
3970        //
3971        // File data (all integers are binary in network byte order)
3972        // *N: 4 : integer token identifying which metadata blob
3973        //     4 : integer size of this blob = N
3974        //     N : raw bytes of this metadata blob
3975        //
3976        // Currently understood blobs (always in network byte order):
3977        //
3978        //     widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
3979        //
3980        // Unrecognized blobs are *ignored*, not errors.
3981        private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
3982                throws IOException {
3983            StringBuilder b = new StringBuilder(512);
3984            StringBuilderPrinter printer = new StringBuilderPrinter(b);
3985            printer.println(Integer.toString(BACKUP_METADATA_VERSION));
3986            printer.println(pkg.packageName);
3987
3988            FileOutputStream fout = new FileOutputStream(destination);
3989            BufferedOutputStream bout = new BufferedOutputStream(fout);
3990            DataOutputStream out = new DataOutputStream(bout);
3991            bout.write(b.toString().getBytes());    // bypassing DataOutputStream
3992
3993            if (widgetData != null && widgetData.length > 0) {
3994                out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
3995                out.writeInt(widgetData.length);
3996                out.write(widgetData);
3997            }
3998            bout.flush();
3999            out.close();
4000
4001            // As with the manifest file, guarantee idempotence of the archive metadata
4002            // for the widget block by using a fixed mtime on the transient file.
4003            destination.setLastModified(0);
4004        }
4005
4006        private void tearDown() {
4007            if (mPkg != null) {
4008                tearDownAgentAndKill(mPkg.applicationInfo);
4009            }
4010        }
4011    }
4012
4013    // Generic driver skeleton for full backup operations
4014    abstract class FullBackupTask implements Runnable {
4015        IFullBackupRestoreObserver mObserver;
4016
4017        FullBackupTask(IFullBackupRestoreObserver observer) {
4018            mObserver = observer;
4019        }
4020
4021        // wrappers for observer use
4022        final void sendStartBackup() {
4023            if (mObserver != null) {
4024                try {
4025                    mObserver.onStartBackup();
4026                } catch (RemoteException e) {
4027                    Slog.w(TAG, "full backup observer went away: startBackup");
4028                    mObserver = null;
4029                }
4030            }
4031        }
4032
4033        final void sendOnBackupPackage(String name) {
4034            if (mObserver != null) {
4035                try {
4036                    // TODO: use a more user-friendly name string
4037                    mObserver.onBackupPackage(name);
4038                } catch (RemoteException e) {
4039                    Slog.w(TAG, "full backup observer went away: backupPackage");
4040                    mObserver = null;
4041                }
4042            }
4043        }
4044
4045        final void sendEndBackup() {
4046            if (mObserver != null) {
4047                try {
4048                    mObserver.onEndBackup();
4049                } catch (RemoteException e) {
4050                    Slog.w(TAG, "full backup observer went away: endBackup");
4051                    mObserver = null;
4052                }
4053            }
4054        }
4055    }
4056
4057    boolean deviceIsEncrypted() {
4058        try {
4059            return mStorageManager.getEncryptionState()
4060                     != StorageManager.ENCRYPTION_STATE_NONE
4061                && mStorageManager.getPasswordType()
4062                     != StorageManager.CRYPT_TYPE_DEFAULT;
4063        } catch (Exception e) {
4064            // If we can't talk to the storagemanager service we have a serious problem; fail
4065            // "secure" i.e. assuming that the device is encrypted.
4066            Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
4067            return true;
4068        }
4069    }
4070
4071    // Full backup task variant used for adb backup
4072    class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
4073        FullBackupEngine mBackupEngine;
4074        final AtomicBoolean mLatch;
4075
4076        ParcelFileDescriptor mOutputFile;
4077        DeflaterOutputStream mDeflater;
4078        boolean mIncludeApks;
4079        boolean mIncludeObbs;
4080        boolean mIncludeShared;
4081        boolean mDoWidgets;
4082        boolean mAllApps;
4083        boolean mIncludeSystem;
4084        boolean mCompress;
4085        ArrayList<String> mPackages;
4086        PackageInfo mCurrentTarget;
4087        String mCurrentPassword;
4088        String mEncryptPassword;
4089        private final int mCurrentOpToken;
4090
4091        PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
4092                boolean includeApks, boolean includeObbs, boolean includeShared,
4093                boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
4094                boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch) {
4095            super(observer);
4096            mCurrentOpToken = generateToken();
4097            mLatch = latch;
4098
4099            mOutputFile = fd;
4100            mIncludeApks = includeApks;
4101            mIncludeObbs = includeObbs;
4102            mIncludeShared = includeShared;
4103            mDoWidgets = doWidgets;
4104            mAllApps = doAllApps;
4105            mIncludeSystem = doSystem;
4106            mPackages = (packages == null)
4107                    ? new ArrayList<String>()
4108                    : new ArrayList<String>(Arrays.asList(packages));
4109            mCurrentPassword = curPassword;
4110            // when backing up, if there is a current backup password, we require that
4111            // the user use a nonempty encryption password as well.  if one is supplied
4112            // in the UI we use that, but if the UI was left empty we fall back to the
4113            // current backup password (which was supplied by the user as well).
4114            if (encryptPassword == null || "".equals(encryptPassword)) {
4115                mEncryptPassword = curPassword;
4116            } else {
4117                mEncryptPassword = encryptPassword;
4118            }
4119            if (MORE_DEBUG) {
4120                Slog.w(TAG, "Encrypting backup with passphrase=" + mEncryptPassword);
4121            }
4122            mCompress = doCompress;
4123        }
4124
4125        void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
4126            for (String pkgName : pkgNames) {
4127                if (!set.containsKey(pkgName)) {
4128                    try {
4129                        PackageInfo info = mPackageManager.getPackageInfo(pkgName,
4130                                PackageManager.GET_SIGNATURES);
4131                        set.put(pkgName, info);
4132                    } catch (NameNotFoundException e) {
4133                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
4134                    }
4135                }
4136            }
4137        }
4138
4139        private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
4140                OutputStream ofstream) throws Exception {
4141            // User key will be used to encrypt the master key.
4142            byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE);
4143            SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt,
4144                    PBKDF2_HASH_ROUNDS);
4145
4146            // the master key is random for each backup
4147            byte[] masterPw = new byte[256 / 8];
4148            mRng.nextBytes(masterPw);
4149            byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE);
4150
4151            // primary encryption of the datastream with the random key
4152            Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
4153            SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES");
4154            c.init(Cipher.ENCRYPT_MODE, masterKeySpec);
4155            OutputStream finalOutput = new CipherOutputStream(ofstream, c);
4156
4157            // line 4: name of encryption algorithm
4158            headerbuf.append(ENCRYPTION_ALGORITHM_NAME);
4159            headerbuf.append('\n');
4160            // line 5: user password salt [hex]
4161            headerbuf.append(byteArrayToHex(newUserSalt));
4162            headerbuf.append('\n');
4163            // line 6: master key checksum salt [hex]
4164            headerbuf.append(byteArrayToHex(checksumSalt));
4165            headerbuf.append('\n');
4166            // line 7: number of PBKDF2 rounds used [decimal]
4167            headerbuf.append(PBKDF2_HASH_ROUNDS);
4168            headerbuf.append('\n');
4169
4170            // line 8: IV of the user key [hex]
4171            Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding");
4172            mkC.init(Cipher.ENCRYPT_MODE, userKey);
4173
4174            byte[] IV = mkC.getIV();
4175            headerbuf.append(byteArrayToHex(IV));
4176            headerbuf.append('\n');
4177
4178            // line 9: master IV + key blob, encrypted by the user key [hex].  Blob format:
4179            //    [byte] IV length = Niv
4180            //    [array of Niv bytes] IV itself
4181            //    [byte] master key length = Nmk
4182            //    [array of Nmk bytes] master key itself
4183            //    [byte] MK checksum hash length = Nck
4184            //    [array of Nck bytes] master key checksum hash
4185            //
4186            // The checksum is the (master key + checksum salt), run through the
4187            // stated number of PBKDF2 rounds
4188            IV = c.getIV();
4189            byte[] mk = masterKeySpec.getEncoded();
4190            byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(),
4191                    checksumSalt, PBKDF2_HASH_ROUNDS);
4192
4193            ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length
4194                    + checksum.length + 3);
4195            DataOutputStream mkOut = new DataOutputStream(blob);
4196            mkOut.writeByte(IV.length);
4197            mkOut.write(IV);
4198            mkOut.writeByte(mk.length);
4199            mkOut.write(mk);
4200            mkOut.writeByte(checksum.length);
4201            mkOut.write(checksum);
4202            mkOut.flush();
4203            byte[] encryptedMk = mkC.doFinal(blob.toByteArray());
4204            headerbuf.append(byteArrayToHex(encryptedMk));
4205            headerbuf.append('\n');
4206
4207            return finalOutput;
4208        }
4209
4210        private void finalizeBackup(OutputStream out) {
4211            try {
4212                // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes.
4213                byte[] eof = new byte[512 * 2]; // newly allocated == zero filled
4214                out.write(eof);
4215            } catch (IOException e) {
4216                Slog.w(TAG, "Error attempting to finalize backup stream");
4217            }
4218        }
4219
4220        @Override
4221        public void run() {
4222            Slog.i(TAG, "--- Performing full-dataset adb backup ---");
4223
4224            TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
4225            FullBackupObbConnection obbConnection = new FullBackupObbConnection();
4226            obbConnection.establish();  // we'll want this later
4227
4228            sendStartBackup();
4229
4230            // doAllApps supersedes the package set if any
4231            if (mAllApps) {
4232                List<PackageInfo> allPackages = mPackageManager.getInstalledPackages(
4233                        PackageManager.GET_SIGNATURES);
4234                for (int i = 0; i < allPackages.size(); i++) {
4235                    PackageInfo pkg = allPackages.get(i);
4236                    // Exclude system apps if we've been asked to do so
4237                    if (mIncludeSystem == true
4238                            || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
4239                        packagesToBackup.put(pkg.packageName, pkg);
4240                    }
4241                }
4242            }
4243
4244            // If we're doing widget state as well, ensure that we have all the involved
4245            // host & provider packages in the set
4246            if (mDoWidgets) {
4247                // TODO: http://b/22388012
4248                List<String> pkgs =
4249                        AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_SYSTEM);
4250                if (pkgs != null) {
4251                    if (MORE_DEBUG) {
4252                        Slog.i(TAG, "Adding widget participants to backup set:");
4253                        StringBuilder sb = new StringBuilder(128);
4254                        sb.append("   ");
4255                        for (String s : pkgs) {
4256                            sb.append(' ');
4257                            sb.append(s);
4258                        }
4259                        Slog.i(TAG, sb.toString());
4260                    }
4261                    addPackagesToSet(packagesToBackup, pkgs);
4262                }
4263            }
4264
4265            // Now process the command line argument packages, if any. Note that explicitly-
4266            // named system-partition packages will be included even if includeSystem was
4267            // set to false.
4268            if (mPackages != null) {
4269                addPackagesToSet(packagesToBackup, mPackages);
4270            }
4271
4272            // Now we cull any inapplicable / inappropriate packages from the set.  This
4273            // includes the special shared-storage agent package; we handle that one
4274            // explicitly at the end of the backup pass.
4275            Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
4276            while (iter.hasNext()) {
4277                PackageInfo pkg = iter.next().getValue();
4278                if (!appIsEligibleForBackup(pkg.applicationInfo)
4279                        || appIsStopped(pkg.applicationInfo)
4280                        || appIsKeyValueOnly(pkg)) {
4281                    iter.remove();
4282                }
4283            }
4284
4285            // flatten the set of packages now so we can explicitly control the ordering
4286            ArrayList<PackageInfo> backupQueue =
4287                    new ArrayList<PackageInfo>(packagesToBackup.values());
4288            FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
4289            OutputStream out = null;
4290
4291            PackageInfo pkg = null;
4292            try {
4293                boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
4294
4295                // Only allow encrypted backups of encrypted devices
4296                if (deviceIsEncrypted() && !encrypting) {
4297                    Slog.e(TAG, "Unencrypted backup of encrypted device; aborting");
4298                    return;
4299                }
4300
4301                OutputStream finalOutput = ofstream;
4302
4303                // Verify that the given password matches the currently-active
4304                // backup password, if any
4305                if (!backupPasswordMatches(mCurrentPassword)) {
4306                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
4307                    return;
4308                }
4309
4310                // Write the global file header.  All strings are UTF-8 encoded; lines end
4311                // with a '\n' byte.  Actual backup data begins immediately following the
4312                // final '\n'.
4313                //
4314                // line 1: "ANDROID BACKUP"
4315                // line 2: backup file format version, currently "2"
4316                // line 3: compressed?  "0" if not compressed, "1" if compressed.
4317                // line 4: name of encryption algorithm [currently only "none" or "AES-256"]
4318                //
4319                // When line 4 is not "none", then additional header data follows:
4320                //
4321                // line 5: user password salt [hex]
4322                // line 6: master key checksum salt [hex]
4323                // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal]
4324                // line 8: IV of the user key [hex]
4325                // line 9: master key blob [hex]
4326                //     IV of the master key, master key itself, master key checksum hash
4327                //
4328                // The master key checksum is the master key plus its checksum salt, run through
4329                // 10k rounds of PBKDF2.  This is used to verify that the user has supplied the
4330                // correct password for decrypting the archive:  the master key decrypted from
4331                // the archive using the user-supplied password is also run through PBKDF2 in
4332                // this way, and if the result does not match the checksum as stored in the
4333                // archive, then we know that the user-supplied password does not match the
4334                // archive's.
4335                StringBuilder headerbuf = new StringBuilder(1024);
4336
4337                headerbuf.append(BACKUP_FILE_HEADER_MAGIC);
4338                headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n
4339                headerbuf.append(mCompress ? "\n1\n" : "\n0\n");
4340
4341                try {
4342                    // Set up the encryption stage if appropriate, and emit the correct header
4343                    if (encrypting) {
4344                        finalOutput = emitAesBackupHeader(headerbuf, finalOutput);
4345                    } else {
4346                        headerbuf.append("none\n");
4347                    }
4348
4349                    byte[] header = headerbuf.toString().getBytes("UTF-8");
4350                    ofstream.write(header);
4351
4352                    // Set up the compression stage feeding into the encryption stage (if any)
4353                    if (mCompress) {
4354                        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
4355                        finalOutput = new DeflaterOutputStream(finalOutput, deflater, true);
4356                    }
4357
4358                    out = finalOutput;
4359                } catch (Exception e) {
4360                    // Should never happen!
4361                    Slog.e(TAG, "Unable to emit archive header", e);
4362                    return;
4363                }
4364
4365                // Shared storage if requested
4366                if (mIncludeShared) {
4367                    try {
4368                        pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
4369                        backupQueue.add(pkg);
4370                    } catch (NameNotFoundException e) {
4371                        Slog.e(TAG, "Unable to find shared-storage backup handler");
4372                    }
4373                }
4374
4375                // Now actually run the constructed backup sequence
4376                int N = backupQueue.size();
4377                for (int i = 0; i < N; i++) {
4378                    pkg = backupQueue.get(i);
4379                    final boolean isSharedStorage =
4380                            pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
4381
4382                    mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks, this, Long.MAX_VALUE, mCurrentOpToken);
4383                    sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
4384
4385                    // Don't need to check preflight result as there is no preflight hook.
4386                    mCurrentTarget = pkg;
4387                    mBackupEngine.backupOnePackage();
4388
4389                    // after the app's agent runs to handle its private filesystem
4390                    // contents, back up any OBB content it has on its behalf.
4391                    if (mIncludeObbs) {
4392                        boolean obbOkay = obbConnection.backupObbs(pkg, out);
4393                        if (!obbOkay) {
4394                            throw new RuntimeException("Failure writing OBB stack for " + pkg);
4395                        }
4396                    }
4397                }
4398
4399                // Done!
4400                finalizeBackup(out);
4401            } catch (RemoteException e) {
4402                Slog.e(TAG, "App died during full backup");
4403            } catch (Exception e) {
4404                Slog.e(TAG, "Internal exception during full backup", e);
4405            } finally {
4406                try {
4407                    if (out != null) {
4408                        out.flush();
4409                        out.close();
4410                    }
4411                    mOutputFile.close();
4412                } catch (IOException e) {
4413                    /* nothing we can do about this */
4414                }
4415                synchronized (mLatch) {
4416                    mLatch.set(true);
4417                    mLatch.notifyAll();
4418                }
4419                sendEndBackup();
4420                obbConnection.tearDown();
4421                if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
4422                mWakelock.release();
4423            }
4424        }
4425
4426        // BackupRestoreTask methods, used for timeout handling
4427        @Override
4428        public void execute() {
4429            // Unused
4430        }
4431
4432        @Override
4433        public void operationComplete(long result) {
4434            // Unused
4435        }
4436
4437        @Override
4438        public void handleCancel(boolean cancelAll) {
4439            final PackageInfo target = mCurrentTarget;
4440            if (DEBUG) {
4441                Slog.w(TAG, "adb backup cancel of " + target);
4442            }
4443            if (target != null) {
4444                tearDownAgentAndKill(mCurrentTarget.applicationInfo);
4445            }
4446            removeOperation(mCurrentOpToken);
4447        }
4448    }
4449
4450    /**
4451     * Full backup task extension used for transport-oriented operation.
4452     *
4453     * Flow:
4454     * For each requested package:
4455     *     - Spin off a new SinglePackageBackupRunner (mBackupRunner) for the current package.
4456     *     - Wait until preflight is complete. (mBackupRunner.getPreflightResultBlocking())
4457     *     - If preflight data size is within limit, start reading data from agent pipe and writing
4458     *       to transport pipe. While there is data to send, call transport.sendBackupData(int) to
4459     *       tell the transport how many bytes to expect on its pipe.
4460     *     - After sending all data, call transport.finishBackup() if things went well. And
4461     *       transport.cancelFullBackup() otherwise.
4462     *
4463     * Interactions with mCurrentOperations:
4464     *     - An entry for this object is added to mCurrentOperations for the entire lifetime of this
4465     *       object. Used to cancel the operation.
4466     *     - SinglePackageBackupRunner and SinglePackageBackupPreflight will put ephemeral entries
4467     *       to get timeouts or operation complete callbacks.
4468     *
4469     * Handling cancels:
4470     *     - The contract we provide is that the task won't interact with the transport after
4471     *       handleCancel() is done executing.
4472     *     - This task blocks at 3 points: 1. Preflight result check 2. Reading on agent side pipe
4473     *       and 3. Get backup result from mBackupRunner.
4474     *     - Bubbling up handleCancel to mBackupRunner handles all 3: 1. Calls handleCancel on the
4475     *       preflight operation which counts down on the preflight latch. 2. Tears down the agent,
4476     *       so read() returns -1. 3. Notifies mCurrentOpLock which unblocks
4477     *       mBackupRunner.getBackupResultBlocking().
4478     */
4479    class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
4480        static final String TAG = "PFTBT";
4481
4482        private final Object mCancelLock = new Object();
4483
4484        ArrayList<PackageInfo> mPackages;
4485        PackageInfo mCurrentPackage;
4486        boolean mUpdateSchedule;
4487        CountDownLatch mLatch;
4488        FullBackupJob mJob;             // if a scheduled job needs to be finished afterwards
4489        IBackupObserver mBackupObserver;
4490        IBackupManagerMonitor mMonitor;
4491        boolean mUserInitiated;
4492        private volatile IBackupTransport mTransport;
4493        SinglePackageBackupRunner mBackupRunner;
4494        private final int mBackupRunnerOpToken;
4495
4496        // This is true when a backup operation for some package is in progress.
4497        private volatile boolean mIsDoingBackup;
4498        private volatile boolean mCancelAll;
4499        private final int mCurrentOpToken;
4500
4501        PerformFullTransportBackupTask(IFullBackupRestoreObserver observer,
4502                String[] whichPackages, boolean updateSchedule,
4503                FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver,
4504                IBackupManagerMonitor monitor, boolean userInitiated) {
4505            super(observer);
4506            mUpdateSchedule = updateSchedule;
4507            mLatch = latch;
4508            mJob = runningJob;
4509            mPackages = new ArrayList<PackageInfo>(whichPackages.length);
4510            mBackupObserver = backupObserver;
4511            mMonitor = monitor;
4512            mUserInitiated = userInitiated;
4513            mCurrentOpToken = generateToken();
4514            mBackupRunnerOpToken = generateToken();
4515
4516            registerTask();
4517
4518            for (String pkg : whichPackages) {
4519                try {
4520                    PackageInfo info = mPackageManager.getPackageInfo(pkg,
4521                            PackageManager.GET_SIGNATURES);
4522                    mCurrentPackage = info;
4523                    if (!appIsEligibleForBackup(info.applicationInfo)) {
4524                        // Cull any packages that have indicated that backups are not permitted,
4525                        // that run as system-domain uids but do not define their own backup agents,
4526                        // as well as any explicit mention of the 'special' shared-storage agent
4527                        // package (we handle that one at the end).
4528                        if (MORE_DEBUG) {
4529                            Slog.d(TAG, "Ignoring ineligible package " + pkg);
4530                        }
4531                        sendBackupOnPackageResult(mBackupObserver, pkg,
4532                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4533                        continue;
4534                    } else if (!appGetsFullBackup(info)) {
4535                        // Cull any packages that are found in the queue but now aren't supposed
4536                        // to get full-data backup operations.
4537                        if (MORE_DEBUG) {
4538                            Slog.d(TAG, "Ignoring full-data backup of key/value participant "
4539                                    + pkg);
4540                        }
4541                        sendBackupOnPackageResult(mBackupObserver, pkg,
4542                                BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4543                        continue;
4544                    } else if (appIsStopped(info.applicationInfo)) {
4545                        // Cull any packages in the 'stopped' state: they've either just been
4546                        // installed or have explicitly been force-stopped by the user.  In both
4547                        // cases we do not want to launch them for backup.
4548                        if (MORE_DEBUG) {
4549                            Slog.d(TAG, "Ignoring stopped package " + pkg);
4550                        }
4551                        sendBackupOnPackageResult(mBackupObserver, pkg,
4552                                BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4553                        continue;
4554                    }
4555                    mPackages.add(info);
4556                } catch (NameNotFoundException e) {
4557                    Slog.i(TAG, "Requested package " + pkg + " not found; ignoring");
4558                }
4559            }
4560        }
4561
4562        private void registerTask() {
4563            synchronized (mCurrentOpLock) {
4564                Slog.d(TAG, "backupmanager pftbt token=" + Integer.toHexString(mCurrentOpToken));
4565                mCurrentOperations.put(mCurrentOpToken, new Operation(OP_PENDING, this,
4566                        OP_TYPE_BACKUP));
4567            }
4568        }
4569
4570        private void unregisterTask() {
4571            removeOperation(mCurrentOpToken);
4572        }
4573
4574        @Override
4575        public void execute() {
4576            // Nothing to do.
4577        }
4578
4579        @Override
4580        public void handleCancel(boolean cancelAll) {
4581            synchronized (mCancelLock) {
4582                // We only support 'cancelAll = true' case for this task. Cancelling of a single package
4583
4584                // due to timeout is handled by SinglePackageBackupRunner and SinglePackageBackupPreflight.
4585
4586                if (!cancelAll) {
4587                    Slog.wtf(TAG, "Expected cancelAll to be true.");
4588                }
4589
4590                if (mCancelAll) {
4591                    Slog.d(TAG, "Ignoring duplicate cancel call.");
4592                    return;
4593                }
4594
4595                mCancelAll = true;
4596                if (mIsDoingBackup) {
4597                    BackupManagerService.this.handleCancel(mBackupRunnerOpToken, cancelAll);
4598                    try {
4599                        mTransport.cancelFullBackup();
4600                    } catch (RemoteException e) {
4601                        Slog.w(TAG, "Error calling cancelFullBackup() on transport: " + e);
4602                        // Can't do much.
4603                    }
4604                }
4605            }
4606        }
4607
4608        @Override
4609        public void operationComplete(long result) {
4610            // Nothing to do.
4611        }
4612
4613        @Override
4614        public void run() {
4615
4616            // data from the app, passed to us for bridging to the transport
4617            ParcelFileDescriptor[] enginePipes = null;
4618
4619            // Pipe through which we write data to the transport
4620            ParcelFileDescriptor[] transportPipes = null;
4621
4622            long backoff = 0;
4623            int backupRunStatus = BackupManager.SUCCESS;
4624
4625            try {
4626                if (!mEnabled || !mProvisioned) {
4627                    // Backups are globally disabled, so don't proceed.
4628                    if (DEBUG) {
4629                        Slog.i(TAG, "full backup requested but e=" + mEnabled
4630                                + " p=" + mProvisioned + "; ignoring");
4631                    }
4632                    mUpdateSchedule = false;
4633                    backupRunStatus = BackupManager.ERROR_BACKUP_NOT_ALLOWED;
4634                    return;
4635                }
4636
4637                mTransport = mTransportManager.getCurrentTransportBinder();
4638                if (mTransport == null) {
4639                    Slog.w(TAG, "Transport not present; full data backup not performed");
4640                    backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
4641                    return;
4642                }
4643
4644                // Set up to send data to the transport
4645                final int N = mPackages.size();
4646                final byte[] buffer = new byte[8192];
4647                for (int i = 0; i < N; i++) {
4648                    PackageInfo currentPackage = mPackages.get(i);
4649                    String packageName = currentPackage.packageName;
4650                    if (DEBUG) {
4651                        Slog.i(TAG, "Initiating full-data transport backup of " + packageName);
4652                    }
4653                    EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, packageName);
4654
4655                    transportPipes = ParcelFileDescriptor.createPipe();
4656
4657                    // Tell the transport the data's coming
4658                    int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
4659                    int backupPackageStatus;
4660                    long quota = Long.MAX_VALUE;
4661                    synchronized (mCancelLock) {
4662                        if (mCancelAll) {
4663                            break;
4664                        }
4665                        backupPackageStatus = mTransport.performFullBackup(currentPackage,
4666                                transportPipes[0], flags);
4667
4668                        if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
4669                            quota = mTransport.getBackupQuota(currentPackage.packageName,
4670                                    true /* isFullBackup */);
4671                            // Now set up the backup engine / data source end of things
4672                            enginePipes = ParcelFileDescriptor.createPipe();
4673                            mBackupRunner =
4674                                    new SinglePackageBackupRunner(enginePipes[1], currentPackage,
4675                                            mTransport, quota, mBackupRunnerOpToken);
4676                            // The runner dup'd the pipe half, so we close it here
4677                            enginePipes[1].close();
4678                            enginePipes[1] = null;
4679
4680                            mIsDoingBackup = true;
4681                        }
4682                    }
4683                    if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
4684
4685                        // The transport has its own copy of the read end of the pipe,
4686                        // so close ours now
4687                        transportPipes[0].close();
4688                        transportPipes[0] = null;
4689
4690                        // Spin off the runner to fetch the app's data and pipe it
4691                        // into the engine pipes
4692                        (new Thread(mBackupRunner, "package-backup-bridge")).start();
4693
4694                        // Read data off the engine pipe and pass it to the transport
4695                        // pipe until we hit EOD on the input stream.  We do not take
4696                        // close() responsibility for these FDs into these stream wrappers.
4697                        FileInputStream in = new FileInputStream(
4698                                enginePipes[0].getFileDescriptor());
4699                        FileOutputStream out = new FileOutputStream(
4700                                transportPipes[1].getFileDescriptor());
4701                        long totalRead = 0;
4702                        final long preflightResult = mBackupRunner.getPreflightResultBlocking();
4703                        // Preflight result is negative if some error happened on preflight.
4704                        if (preflightResult < 0) {
4705                            if (MORE_DEBUG) {
4706                                Slog.d(TAG, "Backup error after preflight of package "
4707                                        + packageName + ": " + preflightResult
4708                                        + ", not running backup.");
4709                            }
4710                            backupPackageStatus = (int) preflightResult;
4711                        } else {
4712                            int nRead = 0;
4713                            do {
4714                                nRead = in.read(buffer);
4715                                if (MORE_DEBUG) {
4716                                    Slog.v(TAG, "in.read(buffer) from app: " + nRead);
4717                                }
4718                                if (nRead > 0) {
4719                                    out.write(buffer, 0, nRead);
4720                                    synchronized (mCancelLock) {
4721                                        if (!mCancelAll) {
4722                                            backupPackageStatus = mTransport.sendBackupData(nRead);
4723                                        }
4724                                    }
4725                                    totalRead += nRead;
4726                                    if (mBackupObserver != null && preflightResult > 0) {
4727                                        sendBackupOnUpdate(mBackupObserver, packageName,
4728                                                new BackupProgress(preflightResult, totalRead));
4729                                    }
4730                                }
4731                            } while (nRead > 0
4732                                    && backupPackageStatus == BackupTransport.TRANSPORT_OK);
4733                            // Despite preflight succeeded, package still can hit quota on flight.
4734                            if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
4735                                Slog.w(TAG, "Package hit quota limit in-flight " + packageName
4736                                        + ": " + totalRead + " of " + quota);
4737                                mBackupRunner.sendQuotaExceeded(totalRead, quota);
4738                            }
4739                        }
4740
4741                        final int backupRunnerResult = mBackupRunner.getBackupResultBlocking();
4742
4743                        synchronized (mCancelLock) {
4744                            mIsDoingBackup = false;
4745                            // If mCancelCurrent is true, we have already called cancelFullBackup().
4746                            if (!mCancelAll) {
4747                                if (backupRunnerResult == BackupTransport.TRANSPORT_OK) {
4748                                    // If we were otherwise in a good state, now interpret the final
4749                                    // result based on what finishBackup() returns.  If we're in a
4750                                    // failure case already, preserve that result and ignore whatever
4751                                    // finishBackup() reports.
4752                                    final int finishResult = mTransport.finishBackup();
4753                                    if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
4754                                        backupPackageStatus = finishResult;
4755                                    }
4756                                } else {
4757                                    mTransport.cancelFullBackup();
4758                                }
4759                            }
4760                        }
4761
4762                        // A transport-originated error here means that we've hit an error that the
4763                        // runner doesn't know about, so it's still moving data but we're pulling the
4764                        // rug out from under it.  Don't ask for its result:  we already know better
4765                        // and we'll hang if we block waiting for it, since it relies on us to
4766                        // read back the data it's writing into the engine.  Just proceed with
4767                        // a graceful failure.  The runner/engine mechanism will tear itself
4768                        // down cleanly when we close the pipes from this end.  Transport-level
4769                        // errors take precedence over agent/app-specific errors for purposes of
4770                        // determining our course of action.
4771                        if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
4772                            // We still could fail in backup runner thread.
4773                            if (backupRunnerResult != BackupTransport.TRANSPORT_OK) {
4774                                // If there was an error in runner thread and
4775                                // not TRANSPORT_ERROR here, overwrite it.
4776                                backupPackageStatus = backupRunnerResult;
4777                            }
4778                        } else {
4779                            if (MORE_DEBUG) {
4780                                Slog.i(TAG, "Transport-level failure; cancelling agent work");
4781                            }
4782                        }
4783
4784                        if (MORE_DEBUG) {
4785                            Slog.i(TAG, "Done delivering backup data: result="
4786                                    + backupPackageStatus);
4787                        }
4788
4789                        if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
4790                            Slog.e(TAG, "Error " + backupPackageStatus + " backing up "
4791                                    + packageName);
4792                        }
4793
4794                        // Also ask the transport how long it wants us to wait before
4795                        // moving on to the next package, if any.
4796                        backoff = mTransport.requestFullBackupTime();
4797                        if (DEBUG_SCHEDULING) {
4798                            Slog.i(TAG, "Transport suggested backoff=" + backoff);
4799                        }
4800
4801                    }
4802
4803                    // Roll this package to the end of the backup queue if we're
4804                    // in a queue-driven mode (regardless of success/failure)
4805                    if (mUpdateSchedule) {
4806                        enqueueFullBackup(packageName, System.currentTimeMillis());
4807                    }
4808
4809                    if (backupPackageStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
4810                        sendBackupOnPackageResult(mBackupObserver, packageName,
4811                                BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
4812                        if (DEBUG) {
4813                            Slog.i(TAG, "Transport rejected backup of " + packageName
4814                                    + ", skipping");
4815                        }
4816                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName,
4817                                "transport rejected");
4818                        // Do nothing, clean up, and continue looping.
4819                    } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
4820                        sendBackupOnPackageResult(mBackupObserver, packageName,
4821                                BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
4822                        if (DEBUG) {
4823                            Slog.i(TAG, "Transport quota exceeded for package: " + packageName);
4824                            EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
4825                                    packageName);
4826                        }
4827                        // Do nothing, clean up, and continue looping.
4828                    } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
4829                        sendBackupOnPackageResult(mBackupObserver, packageName,
4830                                BackupManager.ERROR_AGENT_FAILURE);
4831                        Slog.w(TAG, "Application failure for package: " + packageName);
4832                        EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
4833                        tearDownAgentAndKill(currentPackage.applicationInfo);
4834                        // Do nothing, clean up, and continue looping.
4835                    } else if (backupPackageStatus == BackupManager.ERROR_BACKUP_CANCELLED) {
4836                        sendBackupOnPackageResult(mBackupObserver, packageName,
4837                                BackupManager.ERROR_BACKUP_CANCELLED);
4838                        Slog.w(TAG, "Backup cancelled. package=" + packageName +
4839                                ", cancelAll=" + mCancelAll);
4840                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName);
4841                        tearDownAgentAndKill(currentPackage.applicationInfo);
4842                        // Do nothing, clean up, and continue looping.
4843                    } else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
4844                        sendBackupOnPackageResult(mBackupObserver, packageName,
4845                            BackupManager.ERROR_TRANSPORT_ABORTED);
4846                        Slog.w(TAG, "Transport failed; aborting backup: " + backupPackageStatus);
4847                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
4848                        // Abort entire backup pass.
4849                        backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
4850                        return;
4851                    } else {
4852                        // Success!
4853                        sendBackupOnPackageResult(mBackupObserver, packageName,
4854                                BackupManager.SUCCESS);
4855                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, packageName);
4856                        logBackupComplete(packageName);
4857                    }
4858                    cleanUpPipes(transportPipes);
4859                    cleanUpPipes(enginePipes);
4860                    if (currentPackage.applicationInfo != null) {
4861                        Slog.i(TAG, "Unbinding agent in " + packageName);
4862                        addBackupTrace("unbinding " + packageName);
4863                        try {
4864                            mActivityManager.unbindBackupAgent(currentPackage.applicationInfo);
4865                        } catch (RemoteException e) { /* can't happen; activity manager is local */ }
4866                    }
4867                }
4868            } catch (Exception e) {
4869                backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
4870                Slog.w(TAG, "Exception trying full transport backup", e);
4871            } finally {
4872
4873                if (mCancelAll) {
4874                    backupRunStatus = BackupManager.ERROR_BACKUP_CANCELLED;
4875                }
4876
4877                if (DEBUG) {
4878                    Slog.i(TAG, "Full backup completed with status: " + backupRunStatus);
4879                }
4880                sendBackupFinished(mBackupObserver, backupRunStatus);
4881
4882                cleanUpPipes(transportPipes);
4883                cleanUpPipes(enginePipes);
4884
4885                unregisterTask();
4886
4887                if (mJob != null) {
4888                    mJob.finishBackupPass();
4889                }
4890
4891                synchronized (mQueueLock) {
4892                    mRunningFullBackupTask = null;
4893                }
4894
4895                mLatch.countDown();
4896
4897                // Now that we're actually done with schedule-driven work, reschedule
4898                // the next pass based on the new queue state.
4899                if (mUpdateSchedule) {
4900                    scheduleNextFullBackupJob(backoff);
4901                }
4902
4903                Slog.i(BackupManagerService.TAG, "Full data backup pass finished.");
4904                mWakelock.release();
4905            }
4906        }
4907
4908        void cleanUpPipes(ParcelFileDescriptor[] pipes) {
4909            if (pipes != null) {
4910                if (pipes[0] != null) {
4911                    ParcelFileDescriptor fd = pipes[0];
4912                    pipes[0] = null;
4913                    try {
4914                        fd.close();
4915                    } catch (IOException e) {
4916                        Slog.w(TAG, "Unable to close pipe!");
4917                    }
4918                }
4919                if (pipes[1] != null) {
4920                    ParcelFileDescriptor fd = pipes[1];
4921                    pipes[1] = null;
4922                    try {
4923                        fd.close();
4924                    } catch (IOException e) {
4925                        Slog.w(TAG, "Unable to close pipe!");
4926                    }
4927                }
4928            }
4929        }
4930
4931        // Run the backup and pipe it back to the given socket -- expects to run on
4932        // a standalone thread.  The  runner owns this half of the pipe, and closes
4933        // it to indicate EOD to the other end.
4934        class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
4935            final AtomicLong mResult = new AtomicLong(BackupTransport.AGENT_ERROR);
4936            final CountDownLatch mLatch = new CountDownLatch(1);
4937            final IBackupTransport mTransport;
4938            final long mQuota;
4939            private final int mCurrentOpToken;
4940
4941            SinglePackageBackupPreflight(IBackupTransport transport, long quota, int currentOpToken) {
4942                mTransport = transport;
4943                mQuota = quota;
4944                mCurrentOpToken = currentOpToken;
4945            }
4946
4947            @Override
4948            public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
4949                int result;
4950                try {
4951                    prepareOperationTimeout(mCurrentOpToken, TIMEOUT_FULL_BACKUP_INTERVAL,
4952                            this, OP_TYPE_BACKUP_WAIT);
4953                    addBackupTrace("preflighting");
4954                    if (MORE_DEBUG) {
4955                        Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
4956                    }
4957                    agent.doMeasureFullBackup(mQuota, mCurrentOpToken, mBackupManagerBinder);
4958
4959                    // Now wait to get our result back.  If this backstop timeout is reached without
4960                    // the latch being thrown, flow will continue as though a result or "normal"
4961                    // timeout had been produced.  In case of a real backstop timeout, mResult
4962                    // will still contain the value it was constructed with, AGENT_ERROR, which
4963                    // intentionaly falls into the "just report failure" code.
4964                    mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
4965
4966                    long totalSize = mResult.get();
4967                    // If preflight timed out, mResult will contain error code as int.
4968                    if (totalSize < 0) {
4969                        return (int) totalSize;
4970                    }
4971                    if (MORE_DEBUG) {
4972                        Slog.v(TAG, "Got preflight response; size=" + totalSize);
4973                    }
4974
4975                    result = mTransport.checkFullBackupSize(totalSize);
4976                    if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
4977                        if (MORE_DEBUG) {
4978                            Slog.d(TAG, "Package hit quota limit on preflight " +
4979                                    pkg.packageName + ": " + totalSize + " of " + mQuota);
4980                        }
4981                        agent.doQuotaExceeded(totalSize, mQuota);
4982                    }
4983                } catch (Exception e) {
4984                    Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
4985                    result = BackupTransport.AGENT_ERROR;
4986                }
4987                return result;
4988            }
4989
4990            @Override
4991            public void execute() {
4992                // Unused.
4993            }
4994
4995            @Override
4996            public void operationComplete(long result) {
4997                // got the callback, and our preflightFullBackup() method is waiting for the result
4998                if (MORE_DEBUG) {
4999                    Slog.i(TAG, "Preflight op complete, result=" + result);
5000                }
5001                mResult.set(result);
5002                mLatch.countDown();
5003                removeOperation(mCurrentOpToken);
5004            }
5005
5006            @Override
5007            public void handleCancel(boolean cancelAll) {
5008                if (MORE_DEBUG) {
5009                    Slog.i(TAG, "Preflight cancelled; failing");
5010                }
5011                mResult.set(BackupTransport.AGENT_ERROR);
5012                mLatch.countDown();
5013                removeOperation(mCurrentOpToken);
5014            }
5015
5016            @Override
5017            public long getExpectedSizeOrErrorCode() {
5018                try {
5019                    mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
5020                    return mResult.get();
5021                } catch (InterruptedException e) {
5022                    return BackupTransport.NO_MORE_DATA;
5023                }
5024            }
5025        }
5026
5027        class SinglePackageBackupRunner implements Runnable, BackupRestoreTask {
5028            final ParcelFileDescriptor mOutput;
5029            final PackageInfo mTarget;
5030            final SinglePackageBackupPreflight mPreflight;
5031            final CountDownLatch mPreflightLatch;
5032            final CountDownLatch mBackupLatch;
5033            private final int mCurrentOpToken;
5034            private final int mEphemeralToken;
5035            private FullBackupEngine mEngine;
5036            private volatile int mPreflightResult;
5037            private volatile int mBackupResult;
5038            private final long mQuota;
5039            private volatile boolean mIsCancelled;
5040
5041            SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
5042                    IBackupTransport transport, long quota, int currentOpToken) throws IOException {
5043                mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
5044                mTarget = target;
5045                mCurrentOpToken = currentOpToken;
5046                mEphemeralToken = generateToken();
5047                mPreflight = new SinglePackageBackupPreflight(transport, quota, mEphemeralToken);
5048                mPreflightLatch = new CountDownLatch(1);
5049                mBackupLatch = new CountDownLatch(1);
5050                mPreflightResult = BackupTransport.AGENT_ERROR;
5051                mBackupResult = BackupTransport.AGENT_ERROR;
5052                mQuota = quota;
5053                registerTask();
5054            }
5055
5056            void registerTask() {
5057                synchronized (mCurrentOpLock) {
5058                    mCurrentOperations.put(mCurrentOpToken, new Operation(OP_PENDING, this,
5059                            OP_TYPE_BACKUP_WAIT));
5060                }
5061            }
5062
5063            void unregisterTask() {
5064                synchronized (mCurrentOpLock) {
5065                    mCurrentOperations.remove(mCurrentOpToken);
5066                }
5067            }
5068
5069            @Override
5070            public void run() {
5071                FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
5072                mEngine = new FullBackupEngine(out, mPreflight, mTarget, false, this, mQuota, mCurrentOpToken);
5073                try {
5074                    try {
5075                        if (!mIsCancelled) {
5076                            mPreflightResult = mEngine.preflightCheck();
5077                        }
5078                    } finally {
5079                        mPreflightLatch.countDown();
5080                    }
5081                    // If there is no error on preflight, continue backup.
5082                    if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
5083                        if (!mIsCancelled) {
5084                            mBackupResult = mEngine.backupOnePackage();
5085                        }
5086                    }
5087                } catch (Exception e) {
5088                    Slog.e(TAG, "Exception during full package backup of " + mTarget.packageName);
5089                } finally {
5090                    unregisterTask();
5091                    mBackupLatch.countDown();
5092                    try {
5093                        mOutput.close();
5094                    } catch (IOException e) {
5095                        Slog.w(TAG, "Error closing transport pipe in runner");
5096                    }
5097                }
5098            }
5099
5100            public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
5101                mEngine.sendQuotaExceeded(backupDataBytes, quotaBytes);
5102            }
5103
5104            // If preflight succeeded, returns positive number - preflight size,
5105            // otherwise return negative error code.
5106            long getPreflightResultBlocking() {
5107                try {
5108                    mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
5109                    if (mIsCancelled) {
5110                        return BackupManager.ERROR_BACKUP_CANCELLED;
5111                    }
5112                    if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
5113                        return mPreflight.getExpectedSizeOrErrorCode();
5114                    } else {
5115                        return mPreflightResult;
5116                    }
5117                } catch (InterruptedException e) {
5118                    return BackupTransport.AGENT_ERROR;
5119                }
5120            }
5121
5122            int getBackupResultBlocking() {
5123                try {
5124                    mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
5125                    if (mIsCancelled) {
5126                        return BackupManager.ERROR_BACKUP_CANCELLED;
5127                    }
5128                    return mBackupResult;
5129                } catch (InterruptedException e) {
5130                    return BackupTransport.AGENT_ERROR;
5131                }
5132            }
5133
5134
5135            // BackupRestoreTask interface: specifically, timeout detection
5136
5137            @Override
5138            public void execute() { /* intentionally empty */ }
5139
5140            @Override
5141            public void operationComplete(long result) { /* intentionally empty */ }
5142
5143            @Override
5144            public void handleCancel(boolean cancelAll) {
5145                if (DEBUG) {
5146                    Slog.w(TAG, "Full backup cancel of " + mTarget.packageName);
5147                }
5148
5149                mMonitor = monitorEvent(mMonitor,
5150                        BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_TIMEOUT,
5151                        mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
5152                mIsCancelled = true;
5153                // Cancel tasks spun off by this task.
5154                BackupManagerService.this.handleCancel(mEphemeralToken, cancelAll);
5155                tearDownAgentAndKill(mTarget.applicationInfo);
5156                // Free up everyone waiting on this task and its children.
5157                mPreflightLatch.countDown();
5158                mBackupLatch.countDown();
5159                // We are done with this operation.
5160                removeOperation(mCurrentOpToken);
5161            }
5162        }
5163    }
5164
5165    // ----- Full-data backup scheduling -----
5166
5167    /**
5168     * Schedule a job to tell us when it's a good time to run a full backup
5169     */
5170    void scheduleNextFullBackupJob(long transportMinLatency) {
5171        synchronized (mQueueLock) {
5172            if (mFullBackupQueue.size() > 0) {
5173                // schedule the next job at the point in the future when the least-recently
5174                // backed up app comes due for backup again; or immediately if it's already
5175                // due.
5176                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
5177                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
5178                final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
5179                        ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
5180                final long latency = Math.max(transportMinLatency, appLatency);
5181                Runnable r = new Runnable() {
5182                    @Override public void run() {
5183                        FullBackupJob.schedule(mContext, latency);
5184                    }
5185                };
5186                mBackupHandler.postDelayed(r, 2500);
5187            } else {
5188                if (DEBUG_SCHEDULING) {
5189                    Slog.i(TAG, "Full backup queue empty; not scheduling");
5190                }
5191            }
5192        }
5193    }
5194
5195    /**
5196     * Remove a package from the full-data queue.
5197     */
5198    void dequeueFullBackupLocked(String packageName) {
5199        final int N = mFullBackupQueue.size();
5200        for (int i = N-1; i >= 0; i--) {
5201            final FullBackupEntry e = mFullBackupQueue.get(i);
5202            if (packageName.equals(e.packageName)) {
5203                mFullBackupQueue.remove(i);
5204            }
5205        }
5206    }
5207
5208    /**
5209     * Enqueue full backup for the given app, with a note about when it last ran.
5210     */
5211    void enqueueFullBackup(String packageName, long lastBackedUp) {
5212        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
5213        synchronized (mQueueLock) {
5214            // First, sanity check that we aren't adding a duplicate.  Slow but
5215            // straightforward; we'll have at most on the order of a few hundred
5216            // items in this list.
5217            dequeueFullBackupLocked(packageName);
5218
5219            // This is also slow but easy for modest numbers of apps: work backwards
5220            // from the end of the queue until we find an item whose last backup
5221            // time was before this one, then insert this new entry after it.  If we're
5222            // adding something new we don't bother scanning, and just prepend.
5223            int which = -1;
5224            if (lastBackedUp > 0) {
5225                for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
5226                    final FullBackupEntry entry = mFullBackupQueue.get(which);
5227                    if (entry.lastBackup <= lastBackedUp) {
5228                        mFullBackupQueue.add(which + 1, newEntry);
5229                        break;
5230                    }
5231                }
5232            }
5233            if (which < 0) {
5234                // this one is earlier than any existing one, so prepend
5235                mFullBackupQueue.add(0, newEntry);
5236            }
5237        }
5238        writeFullBackupScheduleAsync();
5239    }
5240
5241    private boolean fullBackupAllowable(IBackupTransport transport) {
5242        if (transport == null) {
5243            Slog.w(TAG, "Transport not present; full data backup not performed");
5244            return false;
5245        }
5246
5247        // Don't proceed unless we have already established package metadata
5248        // for the current dataset via a key/value backup pass.
5249        try {
5250            File stateDir = new File(mBaseStateDir, transport.transportDirName());
5251            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
5252            if (pmState.length() <= 0) {
5253                if (DEBUG) {
5254                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
5255                }
5256                return false;
5257            }
5258        } catch (Exception e) {
5259            Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
5260            return false;
5261        }
5262
5263        return true;
5264    }
5265
5266    /**
5267     * Conditions are right for a full backup operation, so run one.  The model we use is
5268     * to perform one app backup per scheduled job execution, and to reschedule the job
5269     * with zero latency as long as conditions remain right and we still have work to do.
5270     *
5271     * <p>This is the "start a full backup operation" entry point called by the scheduled job.
5272     *
5273     * @return Whether ongoing work will continue.  The return value here will be passed
5274     *         along as the return value to the scheduled job's onStartJob() callback.
5275     */
5276    boolean beginFullBackup(FullBackupJob scheduledJob) {
5277        long now = System.currentTimeMillis();
5278        FullBackupEntry entry = null;
5279        long latency = MIN_FULL_BACKUP_INTERVAL;
5280
5281        if (!mEnabled || !mProvisioned) {
5282            // Backups are globally disabled, so don't proceed.  We also don't reschedule
5283            // the job driving automatic backups; that job will be scheduled again when
5284            // the user enables backup.
5285            if (MORE_DEBUG) {
5286                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
5287                        + " p=" + mProvisioned + "; ignoring");
5288            }
5289            return false;
5290        }
5291
5292        // Don't run the backup if we're in battery saver mode, but reschedule
5293        // to try again in the not-so-distant future.
5294        if (mPowerManager.isPowerSaveMode()) {
5295            if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
5296            FullBackupJob.schedule(mContext, KeyValueBackupJob.BATCH_INTERVAL);
5297            return false;
5298        }
5299
5300        if (DEBUG_SCHEDULING) {
5301            Slog.i(TAG, "Beginning scheduled full backup operation");
5302        }
5303
5304        // Great; we're able to run full backup jobs now.  See if we have any work to do.
5305        synchronized (mQueueLock) {
5306            if (mRunningFullBackupTask != null) {
5307                Slog.e(TAG, "Backup triggered but one already/still running!");
5308                return false;
5309            }
5310
5311            // At this point we think that we have work to do, but possibly not right now.
5312            // Any exit without actually running backups will also require that we
5313            // reschedule the job.
5314            boolean runBackup = true;
5315            boolean headBusy;
5316
5317            do {
5318                // Recheck each time, because culling due to ineligibility may
5319                // have emptied the queue.
5320                if (mFullBackupQueue.size() == 0) {
5321                    // no work to do so just bow out
5322                    if (DEBUG) {
5323                        Slog.i(TAG, "Backup queue empty; doing nothing");
5324                    }
5325                    runBackup = false;
5326                    break;
5327                }
5328
5329                headBusy = false;
5330
5331                if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
5332                    if (MORE_DEBUG) {
5333                        Slog.i(TAG, "Preconditions not met; not running full backup");
5334                    }
5335                    runBackup = false;
5336                    // Typically this means we haven't run a key/value backup yet.  Back off
5337                    // full-backup operations by the key/value job's run interval so that
5338                    // next time we run, we are likely to be able to make progress.
5339                    latency = KeyValueBackupJob.BATCH_INTERVAL;
5340                }
5341
5342                if (runBackup) {
5343                    entry = mFullBackupQueue.get(0);
5344                    long timeSinceRun = now - entry.lastBackup;
5345                    runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
5346                    if (!runBackup) {
5347                        // It's too early to back up the next thing in the queue, so bow out
5348                        if (MORE_DEBUG) {
5349                            Slog.i(TAG, "Device ready but too early to back up next app");
5350                        }
5351                        // Wait until the next app in the queue falls due for a full data backup
5352                        latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
5353                        break;  // we know we aren't doing work yet, so bail.
5354                    }
5355
5356                    try {
5357                        PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
5358                        if (!appGetsFullBackup(appInfo)) {
5359                            // The head app isn't supposed to get full-data backups [any more];
5360                            // so we cull it and force a loop around to consider the new head
5361                            // app.
5362                            if (MORE_DEBUG) {
5363                                Slog.i(TAG, "Culling package " + entry.packageName
5364                                        + " in full-backup queue but not eligible");
5365                            }
5366                            mFullBackupQueue.remove(0);
5367                            headBusy = true; // force the while() condition
5368                            continue;
5369                        }
5370
5371                        final int privFlags = appInfo.applicationInfo.privateFlags;
5372                        headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
5373                                && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
5374
5375                        if (headBusy) {
5376                            final long nextEligible = System.currentTimeMillis()
5377                                    + BUSY_BACKOFF_MIN_MILLIS
5378                                    + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
5379                            if (DEBUG_SCHEDULING) {
5380                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
5381                                Slog.i(TAG, "Full backup time but " + entry.packageName
5382                                        + " is busy; deferring to "
5383                                        + sdf.format(new Date(nextEligible)));
5384                            }
5385                            // This relocates the app's entry from the head of the queue to
5386                            // its order-appropriate position further down, so upon looping
5387                            // a new candidate will be considered at the head.
5388                            enqueueFullBackup(entry.packageName,
5389                                    nextEligible - MIN_FULL_BACKUP_INTERVAL);
5390                        }
5391                    } catch (NameNotFoundException nnf) {
5392                        // So, we think we want to back this up, but it turns out the package
5393                        // in question is no longer installed.  We want to drop it from the
5394                        // queue entirely and move on, but if there's nothing else in the queue
5395                        // we should bail entirely.  headBusy cannot have been set to true yet.
5396                        runBackup = (mFullBackupQueue.size() > 1);
5397                    } catch (RemoteException e) {
5398                        // Cannot happen; the Activity Manager is in the same process
5399                    }
5400                }
5401            } while (headBusy);
5402
5403            if (!runBackup) {
5404                if (DEBUG_SCHEDULING) {
5405                    Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
5406                }
5407                final long deferTime = latency;     // pin for the closure
5408                mBackupHandler.post(new Runnable() {
5409                    @Override public void run() {
5410                        FullBackupJob.schedule(mContext, deferTime);
5411                    }
5412                });
5413                return false;
5414            }
5415
5416            // Okay, the top thing is ready for backup now.  Do it.
5417            mFullBackupQueue.remove(0);
5418            CountDownLatch latch = new CountDownLatch(1);
5419            String[] pkg = new String[] {entry.packageName};
5420            mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
5421                    scheduledJob, latch, null, null, false /* userInitiated */);
5422            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
5423            mWakelock.acquire();
5424            (new Thread(mRunningFullBackupTask)).start();
5425        }
5426
5427        return true;
5428    }
5429
5430    // The job scheduler says our constraints don't hold any more,
5431    // so tear down any ongoing backup task right away.
5432    void endFullBackup() {
5433        synchronized (mQueueLock) {
5434            if (mRunningFullBackupTask != null) {
5435                if (DEBUG_SCHEDULING) {
5436                    Slog.i(TAG, "Telling running backup to stop");
5437                }
5438                mRunningFullBackupTask.handleCancel(true);
5439            }
5440        }
5441    }
5442
5443    // ----- Restore infrastructure -----
5444
5445    abstract class RestoreEngine {
5446        static final String TAG = "RestoreEngine";
5447
5448        public static final int SUCCESS = 0;
5449        public static final int TARGET_FAILURE = -2;
5450        public static final int TRANSPORT_FAILURE = -3;
5451
5452        private AtomicBoolean mRunning = new AtomicBoolean(false);
5453        private AtomicInteger mResult = new AtomicInteger(SUCCESS);
5454
5455        public boolean isRunning() {
5456            return mRunning.get();
5457        }
5458
5459        public void setRunning(boolean stillRunning) {
5460            synchronized (mRunning) {
5461                mRunning.set(stillRunning);
5462                mRunning.notifyAll();
5463            }
5464        }
5465
5466        public int waitForResult() {
5467            synchronized (mRunning) {
5468                while (isRunning()) {
5469                    try {
5470                        mRunning.wait();
5471                    } catch (InterruptedException e) {}
5472                }
5473            }
5474            return getResult();
5475        }
5476
5477        public int getResult() {
5478            return mResult.get();
5479        }
5480
5481        public void setResult(int result) {
5482            mResult.set(result);
5483        }
5484
5485        // TODO: abstract restore state and APIs
5486    }
5487
5488    // ----- Full restore from a file/socket -----
5489
5490    // Description of a file in the restore datastream
5491    static class FileMetadata {
5492        String packageName;             // name of the owning app
5493        String installerPackageName;    // name of the market-type app that installed the owner
5494        int type;                       // e.g. BackupAgent.TYPE_DIRECTORY
5495        String domain;                  // e.g. FullBackup.DATABASE_TREE_TOKEN
5496        String path;                    // subpath within the semantic domain
5497        long mode;                      // e.g. 0666 (actually int)
5498        long mtime;                     // last mod time, UTC time_t (actually int)
5499        long size;                      // bytes of content
5500
5501        @Override
5502        public String toString() {
5503            StringBuilder sb = new StringBuilder(128);
5504            sb.append("FileMetadata{");
5505            sb.append(packageName); sb.append(',');
5506            sb.append(type); sb.append(',');
5507            sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
5508            sb.append(size);
5509            sb.append('}');
5510            return sb.toString();
5511        }
5512    }
5513
5514    enum RestorePolicy {
5515        IGNORE,
5516        ACCEPT,
5517        ACCEPT_IF_APK
5518    }
5519
5520    // Full restore engine, used by both adb restore and transport-based full restore
5521    class FullRestoreEngine extends RestoreEngine {
5522        // Task in charge of monitoring timeouts
5523        BackupRestoreTask mMonitorTask;
5524
5525        // Dedicated observer, if any
5526        IFullBackupRestoreObserver mObserver;
5527
5528        // Where we're delivering the file data as we go
5529        IBackupAgent mAgent;
5530
5531        // Are we permitted to only deliver a specific package's metadata?
5532        PackageInfo mOnlyPackage;
5533
5534        boolean mAllowApks;
5535        boolean mAllowObbs;
5536
5537        // Which package are we currently handling data for?
5538        String mAgentPackage;
5539
5540        // Info for working with the target app process
5541        ApplicationInfo mTargetApp;
5542
5543        // Machinery for restoring OBBs
5544        FullBackupObbConnection mObbConnection = null;
5545
5546        // possible handling states for a given package in the restore dataset
5547        final HashMap<String, RestorePolicy> mPackagePolicies
5548                = new HashMap<String, RestorePolicy>();
5549
5550        // installer package names for each encountered app, derived from the manifests
5551        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
5552
5553        // Signatures for a given package found in its manifest file
5554        final HashMap<String, Signature[]> mManifestSignatures
5555                = new HashMap<String, Signature[]>();
5556
5557        // Packages we've already wiped data on when restoring their first file
5558        final HashSet<String> mClearedPackages = new HashSet<String>();
5559
5560        // How much data have we moved?
5561        long mBytes;
5562
5563        // Working buffer
5564        byte[] mBuffer;
5565
5566        // Pipes for moving data
5567        ParcelFileDescriptor[] mPipes = null;
5568
5569        // Widget blob to be restored out-of-band
5570        byte[] mWidgetData = null;
5571
5572        private final int mEphemeralOpToken;
5573
5574        // Runner that can be placed in a separate thread to do in-process
5575        // invocations of the full restore API asynchronously. Used by adb restore.
5576        class RestoreFileRunnable implements Runnable {
5577            IBackupAgent mAgent;
5578            FileMetadata mInfo;
5579            ParcelFileDescriptor mSocket;
5580            int mToken;
5581
5582            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
5583                    ParcelFileDescriptor socket, int token) throws IOException {
5584                mAgent = agent;
5585                mInfo = info;
5586                mToken = token;
5587
5588                // This class is used strictly for process-local binder invocations.  The
5589                // semantics of ParcelFileDescriptor differ in this case; in particular, we
5590                // do not automatically get a 'dup'ed descriptor that we can can continue
5591                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
5592                // before proceeding to do the restore.
5593                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
5594            }
5595
5596            @Override
5597            public void run() {
5598                try {
5599                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
5600                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
5601                            mToken, mBackupManagerBinder);
5602                } catch (RemoteException e) {
5603                    // never happens; this is used strictly for local binder calls
5604                }
5605            }
5606        }
5607
5608        public FullRestoreEngine(BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
5609                PackageInfo onlyPackage, boolean allowApks, boolean allowObbs,
5610                int ephemeralOpToken) {
5611            mEphemeralOpToken = ephemeralOpToken;
5612            mMonitorTask = monitorTask;
5613            mObserver = observer;
5614            mOnlyPackage = onlyPackage;
5615            mAllowApks = allowApks;
5616            mAllowObbs = allowObbs;
5617            mBuffer = new byte[32 * 1024];
5618            mBytes = 0;
5619        }
5620
5621        public IBackupAgent getAgent() {
5622            return mAgent;
5623        }
5624
5625        public byte[] getWidgetData() {
5626            return mWidgetData;
5627        }
5628
5629        public boolean restoreOneFile(InputStream instream, boolean mustKillAgent) {
5630            if (!isRunning()) {
5631                Slog.w(TAG, "Restore engine used after halting");
5632                return false;
5633            }
5634
5635            FileMetadata info;
5636            try {
5637                if (MORE_DEBUG) {
5638                    Slog.v(TAG, "Reading tar header for restoring file");
5639                }
5640                info = readTarHeaders(instream);
5641                if (info != null) {
5642                    if (MORE_DEBUG) {
5643                        dumpFileMetadata(info);
5644                    }
5645
5646                    final String pkg = info.packageName;
5647                    if (!pkg.equals(mAgentPackage)) {
5648                        // In the single-package case, it's a semantic error to expect
5649                        // one app's data but see a different app's on the wire
5650                        if (mOnlyPackage != null) {
5651                            if (!pkg.equals(mOnlyPackage.packageName)) {
5652                                Slog.w(TAG, "Expected data for " + mOnlyPackage
5653                                        + " but saw " + pkg);
5654                                setResult(RestoreEngine.TRANSPORT_FAILURE);
5655                                setRunning(false);
5656                                return false;
5657                            }
5658                        }
5659
5660                        // okay, change in package; set up our various
5661                        // bookkeeping if we haven't seen it yet
5662                        if (!mPackagePolicies.containsKey(pkg)) {
5663                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5664                        }
5665
5666                        // Clean up the previous agent relationship if necessary,
5667                        // and let the observer know we're considering a new app.
5668                        if (mAgent != null) {
5669                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
5670                            // Now we're really done
5671                            tearDownPipes();
5672                            tearDownAgent(mTargetApp);
5673                            mTargetApp = null;
5674                            mAgentPackage = null;
5675                        }
5676                    }
5677
5678                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
5679                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
5680                        mPackageInstallers.put(pkg, info.installerPackageName);
5681                        // We've read only the manifest content itself at this point,
5682                        // so consume the footer before looping around to the next
5683                        // input file
5684                        skipTarPadding(info.size, instream);
5685                        sendOnRestorePackage(pkg);
5686                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
5687                        // Metadata blobs!
5688                        readMetadata(info, instream);
5689                        skipTarPadding(info.size, instream);
5690                    } else {
5691                        // Non-manifest, so it's actual file data.  Is this a package
5692                        // we're ignoring?
5693                        boolean okay = true;
5694                        RestorePolicy policy = mPackagePolicies.get(pkg);
5695                        switch (policy) {
5696                            case IGNORE:
5697                                okay = false;
5698                                break;
5699
5700                            case ACCEPT_IF_APK:
5701                                // If we're in accept-if-apk state, then the first file we
5702                                // see MUST be the apk.
5703                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5704                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
5705                                    // Try to install the app.
5706                                    String installerName = mPackageInstallers.get(pkg);
5707                                    okay = installApk(info, installerName, instream);
5708                                    // good to go; promote to ACCEPT
5709                                    mPackagePolicies.put(pkg, (okay)
5710                                            ? RestorePolicy.ACCEPT
5711                                                    : RestorePolicy.IGNORE);
5712                                    // At this point we've consumed this file entry
5713                                    // ourselves, so just strip the tar footer and
5714                                    // go on to the next file in the input stream
5715                                    skipTarPadding(info.size, instream);
5716                                    return true;
5717                                } else {
5718                                    // File data before (or without) the apk.  We can't
5719                                    // handle it coherently in this case so ignore it.
5720                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5721                                    okay = false;
5722                                }
5723                                break;
5724
5725                            case ACCEPT:
5726                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5727                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
5728                                    // we can take the data without the apk, so we
5729                                    // *want* to do so.  skip the apk by declaring this
5730                                    // one file not-okay without changing the restore
5731                                    // policy for the package.
5732                                    okay = false;
5733                                }
5734                                break;
5735
5736                            default:
5737                                // Something has gone dreadfully wrong when determining
5738                                // the restore policy from the manifest.  Ignore the
5739                                // rest of this package's data.
5740                                Slog.e(TAG, "Invalid policy from manifest");
5741                                okay = false;
5742                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5743                                break;
5744                        }
5745
5746                        // Is it a *file* we need to drop?
5747                        if (!isRestorableFile(info)) {
5748                            okay = false;
5749                        }
5750
5751                        // If the policy is satisfied, go ahead and set up to pipe the
5752                        // data to the agent.
5753                        if (MORE_DEBUG && okay && mAgent != null) {
5754                            Slog.i(TAG, "Reusing existing agent instance");
5755                        }
5756                        if (okay && mAgent == null) {
5757                            if (MORE_DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
5758
5759                            try {
5760                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
5761
5762                                // If we haven't sent any data to this app yet, we probably
5763                                // need to clear it first.  Check that.
5764                                if (!mClearedPackages.contains(pkg)) {
5765                                    // apps with their own backup agents are
5766                                    // responsible for coherently managing a full
5767                                    // restore.
5768                                    if (mTargetApp.backupAgentName == null) {
5769                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
5770                                        clearApplicationDataSynchronous(pkg);
5771                                    } else {
5772                                        if (MORE_DEBUG) Slog.d(TAG, "backup agent ("
5773                                                + mTargetApp.backupAgentName + ") => no clear");
5774                                    }
5775                                    mClearedPackages.add(pkg);
5776                                } else {
5777                                    if (MORE_DEBUG) {
5778                                        Slog.d(TAG, "We've initialized this app already; no clear required");
5779                                    }
5780                                }
5781
5782                                // All set; now set up the IPC and launch the agent
5783                                setUpPipes();
5784                                mAgent = bindToAgentSynchronous(mTargetApp,
5785                                        ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
5786                                mAgentPackage = pkg;
5787                            } catch (IOException e) {
5788                                // fall through to error handling
5789                            } catch (NameNotFoundException e) {
5790                                // fall through to error handling
5791                            }
5792
5793                            if (mAgent == null) {
5794                                Slog.e(TAG, "Unable to create agent for " + pkg);
5795                                okay = false;
5796                                tearDownPipes();
5797                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5798                            }
5799                        }
5800
5801                        // Sanity check: make sure we never give data to the wrong app.  This
5802                        // should never happen but a little paranoia here won't go amiss.
5803                        if (okay && !pkg.equals(mAgentPackage)) {
5804                            Slog.e(TAG, "Restoring data for " + pkg
5805                                    + " but agent is for " + mAgentPackage);
5806                            okay = false;
5807                        }
5808
5809                        // At this point we have an agent ready to handle the full
5810                        // restore data as well as a pipe for sending data to
5811                        // that agent.  Tell the agent to start reading from the
5812                        // pipe.
5813                        if (okay) {
5814                            boolean agentSuccess = true;
5815                            long toCopy = info.size;
5816                            try {
5817                                prepareOperationTimeout(mEphemeralOpToken,
5818                                        TIMEOUT_FULL_BACKUP_INTERVAL, mMonitorTask,
5819                                        OP_TYPE_RESTORE_WAIT);
5820
5821                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
5822                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
5823                                            + " : " + info.path);
5824                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
5825                                            info.size, info.type, info.path, info.mode,
5826                                            info.mtime, mEphemeralOpToken, mBackupManagerBinder);
5827                                } else {
5828                                    if (MORE_DEBUG) Slog.d(TAG, "Invoking agent to restore file "
5829                                            + info.path);
5830                                    // fire up the app's agent listening on the socket.  If
5831                                    // the agent is running in the system process we can't
5832                                    // just invoke it asynchronously, so we provide a thread
5833                                    // for it here.
5834                                    if (mTargetApp.processName.equals("system")) {
5835                                        Slog.d(TAG, "system process agent - spinning a thread");
5836                                        RestoreFileRunnable runner = new RestoreFileRunnable(
5837                                                mAgent, info, mPipes[0], mEphemeralOpToken);
5838                                        new Thread(runner, "restore-sys-runner").start();
5839                                    } else {
5840                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
5841                                                info.domain, info.path, info.mode, info.mtime,
5842                                                mEphemeralOpToken, mBackupManagerBinder);
5843                                    }
5844                                }
5845                            } catch (IOException e) {
5846                                // couldn't dup the socket for a process-local restore
5847                                Slog.d(TAG, "Couldn't establish restore");
5848                                agentSuccess = false;
5849                                okay = false;
5850                            } catch (RemoteException e) {
5851                                // whoops, remote entity went away.  We'll eat the content
5852                                // ourselves, then, and not copy it over.
5853                                Slog.e(TAG, "Agent crashed during full restore");
5854                                agentSuccess = false;
5855                                okay = false;
5856                            }
5857
5858                            // Copy over the data if the agent is still good
5859                            if (okay) {
5860                                if (MORE_DEBUG) {
5861                                    Slog.v(TAG, "  copying to restore agent: "
5862                                            + toCopy + " bytes");
5863                                }
5864                                boolean pipeOkay = true;
5865                                FileOutputStream pipe = new FileOutputStream(
5866                                        mPipes[1].getFileDescriptor());
5867                                while (toCopy > 0) {
5868                                    int toRead = (toCopy > mBuffer.length)
5869                                            ? mBuffer.length : (int)toCopy;
5870                                    int nRead = instream.read(mBuffer, 0, toRead);
5871                                    if (nRead >= 0) mBytes += nRead;
5872                                    if (nRead <= 0) break;
5873                                    toCopy -= nRead;
5874
5875                                    // send it to the output pipe as long as things
5876                                    // are still good
5877                                    if (pipeOkay) {
5878                                        try {
5879                                            pipe.write(mBuffer, 0, nRead);
5880                                        } catch (IOException e) {
5881                                            Slog.e(TAG, "Failed to write to restore pipe: "
5882                                                    + e.getMessage());
5883                                            pipeOkay = false;
5884                                        }
5885                                    }
5886                                }
5887
5888                                // done sending that file!  Now we just need to consume
5889                                // the delta from info.size to the end of block.
5890                                skipTarPadding(info.size, instream);
5891
5892                                // and now that we've sent it all, wait for the remote
5893                                // side to acknowledge receipt
5894                                agentSuccess = waitUntilOperationComplete(mEphemeralOpToken);
5895                            }
5896
5897                            // okay, if the remote end failed at any point, deal with
5898                            // it by ignoring the rest of the restore on it
5899                            if (!agentSuccess) {
5900                                Slog.w(TAG, "Agent failure; ending restore");
5901                                mBackupHandler.removeMessages(MSG_RESTORE_OPERATION_TIMEOUT);
5902                                tearDownPipes();
5903                                tearDownAgent(mTargetApp);
5904                                mAgent = null;
5905                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5906
5907                                // If this was a single-package restore, we halt immediately
5908                                // with an agent error under these circumstances
5909                                if (mOnlyPackage != null) {
5910                                    setResult(RestoreEngine.TARGET_FAILURE);
5911                                    setRunning(false);
5912                                    return false;
5913                                }
5914                            }
5915                        }
5916
5917                        // Problems setting up the agent communication, an explicitly
5918                        // dropped file, or an already-ignored package: skip to the
5919                        // next stream entry by reading and discarding this file.
5920                        if (!okay) {
5921                            if (MORE_DEBUG) Slog.d(TAG, "[discarding file content]");
5922                            long bytesToConsume = (info.size + 511) & ~511;
5923                            while (bytesToConsume > 0) {
5924                                int toRead = (bytesToConsume > mBuffer.length)
5925                                        ? mBuffer.length : (int)bytesToConsume;
5926                                long nRead = instream.read(mBuffer, 0, toRead);
5927                                if (nRead >= 0) mBytes += nRead;
5928                                if (nRead <= 0) break;
5929                                bytesToConsume -= nRead;
5930                            }
5931                        }
5932                    }
5933                }
5934            } catch (IOException e) {
5935                if (DEBUG) Slog.w(TAG, "io exception on restore socket read: " + e.getMessage());
5936                setResult(RestoreEngine.TRANSPORT_FAILURE);
5937                info = null;
5938            }
5939
5940            // If we got here we're either running smoothly or we've finished
5941            if (info == null) {
5942                if (MORE_DEBUG) {
5943                    Slog.i(TAG, "No [more] data for this package; tearing down");
5944                }
5945                tearDownPipes();
5946                setRunning(false);
5947                if (mustKillAgent) {
5948                    tearDownAgent(mTargetApp);
5949                }
5950            }
5951            return (info != null);
5952        }
5953
5954        void setUpPipes() throws IOException {
5955            mPipes = ParcelFileDescriptor.createPipe();
5956        }
5957
5958        void tearDownPipes() {
5959            // Teardown might arise from the inline restore processing or from the asynchronous
5960            // timeout mechanism, and these might race.  Make sure we don't try to close and
5961            // null out the pipes twice.
5962            synchronized (this) {
5963                if (mPipes != null) {
5964                    try {
5965                        mPipes[0].close();
5966                        mPipes[0] = null;
5967                        mPipes[1].close();
5968                        mPipes[1] = null;
5969                    } catch (IOException e) {
5970                        Slog.w(TAG, "Couldn't close agent pipes", e);
5971                    }
5972                    mPipes = null;
5973                }
5974            }
5975        }
5976
5977        void tearDownAgent(ApplicationInfo app) {
5978            if (mAgent != null) {
5979                tearDownAgentAndKill(app);
5980                mAgent = null;
5981            }
5982        }
5983
5984        void handleTimeout() {
5985            tearDownPipes();
5986            setResult(RestoreEngine.TARGET_FAILURE);
5987            setRunning(false);
5988        }
5989
5990        class RestoreInstallObserver extends PackageInstallObserver {
5991            final AtomicBoolean mDone = new AtomicBoolean();
5992            String mPackageName;
5993            int mResult;
5994
5995            public void reset() {
5996                synchronized (mDone) {
5997                    mDone.set(false);
5998                }
5999            }
6000
6001            public void waitForCompletion() {
6002                synchronized (mDone) {
6003                    while (mDone.get() == false) {
6004                        try {
6005                            mDone.wait();
6006                        } catch (InterruptedException e) { }
6007                    }
6008                }
6009            }
6010
6011            int getResult() {
6012                return mResult;
6013            }
6014
6015            @Override
6016            public void onPackageInstalled(String packageName, int returnCode,
6017                    String msg, Bundle extras) {
6018                synchronized (mDone) {
6019                    mResult = returnCode;
6020                    mPackageName = packageName;
6021                    mDone.set(true);
6022                    mDone.notifyAll();
6023                }
6024            }
6025        }
6026
6027        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
6028            final AtomicBoolean mDone = new AtomicBoolean();
6029            int mResult;
6030
6031            public void reset() {
6032                synchronized (mDone) {
6033                    mDone.set(false);
6034                }
6035            }
6036
6037            public void waitForCompletion() {
6038                synchronized (mDone) {
6039                    while (mDone.get() == false) {
6040                        try {
6041                            mDone.wait();
6042                        } catch (InterruptedException e) { }
6043                    }
6044                }
6045            }
6046
6047            @Override
6048            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
6049                synchronized (mDone) {
6050                    mResult = returnCode;
6051                    mDone.set(true);
6052                    mDone.notifyAll();
6053                }
6054            }
6055        }
6056
6057        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
6058        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
6059
6060        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
6061            boolean okay = true;
6062
6063            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
6064
6065            // The file content is an .apk file.  Copy it out to a staging location and
6066            // attempt to install it.
6067            File apkFile = new File(mDataDir, info.packageName);
6068            try {
6069                FileOutputStream apkStream = new FileOutputStream(apkFile);
6070                byte[] buffer = new byte[32 * 1024];
6071                long size = info.size;
6072                while (size > 0) {
6073                    long toRead = (buffer.length < size) ? buffer.length : size;
6074                    int didRead = instream.read(buffer, 0, (int)toRead);
6075                    if (didRead >= 0) mBytes += didRead;
6076                    apkStream.write(buffer, 0, didRead);
6077                    size -= didRead;
6078                }
6079                apkStream.close();
6080
6081                // make sure the installer can read it
6082                apkFile.setReadable(true, false);
6083
6084                // Now install it
6085                Uri packageUri = Uri.fromFile(apkFile);
6086                mInstallObserver.reset();
6087                mPackageManager.installPackage(packageUri, mInstallObserver,
6088                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
6089                        installerPackage);
6090                mInstallObserver.waitForCompletion();
6091
6092                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
6093                    // The only time we continue to accept install of data even if the
6094                    // apk install failed is if we had already determined that we could
6095                    // accept the data regardless.
6096                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
6097                        okay = false;
6098                    }
6099                } else {
6100                    // Okay, the install succeeded.  Make sure it was the right app.
6101                    boolean uninstall = false;
6102                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
6103                        Slog.w(TAG, "Restore stream claimed to include apk for "
6104                                + info.packageName + " but apk was really "
6105                                + mInstallObserver.mPackageName);
6106                        // delete the package we just put in place; it might be fraudulent
6107                        okay = false;
6108                        uninstall = true;
6109                    } else {
6110                        try {
6111                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
6112                                    PackageManager.GET_SIGNATURES);
6113                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
6114                                Slog.w(TAG, "Restore stream contains apk of package "
6115                                        + info.packageName + " but it disallows backup/restore");
6116                                okay = false;
6117                            } else {
6118                                // So far so good -- do the signatures match the manifest?
6119                                Signature[] sigs = mManifestSignatures.get(info.packageName);
6120                                if (signaturesMatch(sigs, pkg)) {
6121                                    // If this is a system-uid app without a declared backup agent,
6122                                    // don't restore any of the file data.
6123                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
6124                                            && (pkg.applicationInfo.backupAgentName == null)) {
6125                                        Slog.w(TAG, "Installed app " + info.packageName
6126                                                + " has restricted uid and no agent");
6127                                        okay = false;
6128                                    }
6129                                } else {
6130                                    Slog.w(TAG, "Installed app " + info.packageName
6131                                            + " signatures do not match restore manifest");
6132                                    okay = false;
6133                                    uninstall = true;
6134                                }
6135                            }
6136                        } catch (NameNotFoundException e) {
6137                            Slog.w(TAG, "Install of package " + info.packageName
6138                                    + " succeeded but now not found");
6139                            okay = false;
6140                        }
6141                    }
6142
6143                    // If we're not okay at this point, we need to delete the package
6144                    // that we just installed.
6145                    if (uninstall) {
6146                        mDeleteObserver.reset();
6147                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
6148                                mDeleteObserver, 0);
6149                        mDeleteObserver.waitForCompletion();
6150                    }
6151                }
6152            } catch (IOException e) {
6153                Slog.e(TAG, "Unable to transcribe restored apk for install");
6154                okay = false;
6155            } finally {
6156                apkFile.delete();
6157            }
6158
6159            return okay;
6160        }
6161
6162        // Given an actual file content size, consume the post-content padding mandated
6163        // by the tar format.
6164        void skipTarPadding(long size, InputStream instream) throws IOException {
6165            long partial = (size + 512) % 512;
6166            if (partial > 0) {
6167                final int needed = 512 - (int)partial;
6168                if (MORE_DEBUG) {
6169                    Slog.i(TAG, "Skipping tar padding: " + needed + " bytes");
6170                }
6171                byte[] buffer = new byte[needed];
6172                if (readExactly(instream, buffer, 0, needed) == needed) {
6173                    mBytes += needed;
6174                } else throw new IOException("Unexpected EOF in padding");
6175            }
6176        }
6177
6178        // Read a widget metadata file, returning the restored blob
6179        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
6180            // Fail on suspiciously large widget dump files
6181            if (info.size > 64 * 1024) {
6182                throw new IOException("Metadata too big; corrupt? size=" + info.size);
6183            }
6184
6185            byte[] buffer = new byte[(int) info.size];
6186            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6187                mBytes += info.size;
6188            } else throw new IOException("Unexpected EOF in widget data");
6189
6190            String[] str = new String[1];
6191            int offset = extractLine(buffer, 0, str);
6192            int version = Integer.parseInt(str[0]);
6193            if (version == BACKUP_MANIFEST_VERSION) {
6194                offset = extractLine(buffer, offset, str);
6195                final String pkg = str[0];
6196                if (info.packageName.equals(pkg)) {
6197                    // Data checks out -- the rest of the buffer is a concatenation of
6198                    // binary blobs as described in the comment at writeAppWidgetData()
6199                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
6200                            offset, buffer.length - offset);
6201                    DataInputStream in = new DataInputStream(bin);
6202                    while (bin.available() > 0) {
6203                        int token = in.readInt();
6204                        int size = in.readInt();
6205                        if (size > 64 * 1024) {
6206                            throw new IOException("Datum "
6207                                    + Integer.toHexString(token)
6208                                    + " too big; corrupt? size=" + info.size);
6209                        }
6210                        switch (token) {
6211                            case BACKUP_WIDGET_METADATA_TOKEN:
6212                            {
6213                                if (MORE_DEBUG) {
6214                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
6215                                }
6216                                mWidgetData = new byte[size];
6217                                in.read(mWidgetData);
6218                                break;
6219                            }
6220                            default:
6221                            {
6222                                if (DEBUG) {
6223                                    Slog.i(TAG, "Ignoring metadata blob "
6224                                            + Integer.toHexString(token)
6225                                            + " for " + info.packageName);
6226                                }
6227                                in.skipBytes(size);
6228                                break;
6229                            }
6230                        }
6231                    }
6232                } else {
6233                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
6234                            + " but widget data for " + pkg);
6235                }
6236            } else {
6237                Slog.w(TAG, "Unsupported metadata version " + version);
6238            }
6239        }
6240
6241        // Returns a policy constant
6242        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
6243                throws IOException {
6244            // Fail on suspiciously large manifest files
6245            if (info.size > 64 * 1024) {
6246                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
6247            }
6248
6249            byte[] buffer = new byte[(int) info.size];
6250            if (MORE_DEBUG) {
6251                Slog.i(TAG, "   readAppManifest() looking for " + info.size + " bytes, "
6252                        + mBytes + " already consumed");
6253            }
6254            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6255                mBytes += info.size;
6256            } else throw new IOException("Unexpected EOF in manifest");
6257
6258            RestorePolicy policy = RestorePolicy.IGNORE;
6259            String[] str = new String[1];
6260            int offset = 0;
6261
6262            try {
6263                offset = extractLine(buffer, offset, str);
6264                int version = Integer.parseInt(str[0]);
6265                if (version == BACKUP_MANIFEST_VERSION) {
6266                    offset = extractLine(buffer, offset, str);
6267                    String manifestPackage = str[0];
6268                    // TODO: handle <original-package>
6269                    if (manifestPackage.equals(info.packageName)) {
6270                        offset = extractLine(buffer, offset, str);
6271                        version = Integer.parseInt(str[0]);  // app version
6272                        offset = extractLine(buffer, offset, str);
6273                        // This is the platform version, which we don't use, but we parse it
6274                        // as a safety against corruption in the manifest.
6275                        Integer.parseInt(str[0]);
6276                        offset = extractLine(buffer, offset, str);
6277                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
6278                        offset = extractLine(buffer, offset, str);
6279                        boolean hasApk = str[0].equals("1");
6280                        offset = extractLine(buffer, offset, str);
6281                        int numSigs = Integer.parseInt(str[0]);
6282                        if (numSigs > 0) {
6283                            Signature[] sigs = new Signature[numSigs];
6284                            for (int i = 0; i < numSigs; i++) {
6285                                offset = extractLine(buffer, offset, str);
6286                                sigs[i] = new Signature(str[0]);
6287                            }
6288                            mManifestSignatures.put(info.packageName, sigs);
6289
6290                            // Okay, got the manifest info we need...
6291                            try {
6292                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
6293                                        info.packageName, PackageManager.GET_SIGNATURES);
6294                                // Fall through to IGNORE if the app explicitly disallows backup
6295                                final int flags = pkgInfo.applicationInfo.flags;
6296                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
6297                                    // Restore system-uid-space packages only if they have
6298                                    // defined a custom backup agent
6299                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
6300                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
6301                                        // Verify signatures against any installed version; if they
6302                                        // don't match, then we fall though and ignore the data.  The
6303                                        // signatureMatch() method explicitly ignores the signature
6304                                        // check for packages installed on the system partition, because
6305                                        // such packages are signed with the platform cert instead of
6306                                        // the app developer's cert, so they're different on every
6307                                        // device.
6308                                        if (signaturesMatch(sigs, pkgInfo)) {
6309                                            if ((pkgInfo.applicationInfo.flags
6310                                                    & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
6311                                                Slog.i(TAG, "Package has restoreAnyVersion; taking data");
6312                                                policy = RestorePolicy.ACCEPT;
6313                                            } else if (pkgInfo.versionCode >= version) {
6314                                                Slog.i(TAG, "Sig + version match; taking data");
6315                                                policy = RestorePolicy.ACCEPT;
6316                                            } else {
6317                                                // The data is from a newer version of the app than
6318                                                // is presently installed.  That means we can only
6319                                                // use it if the matching apk is also supplied.
6320                                                if (mAllowApks) {
6321                                                    Slog.i(TAG, "Data version " + version
6322                                                            + " is newer than installed version "
6323                                                            + pkgInfo.versionCode
6324                                                            + " - requiring apk");
6325                                                    policy = RestorePolicy.ACCEPT_IF_APK;
6326                                                } else {
6327                                                    Slog.i(TAG, "Data requires newer version "
6328                                                            + version + "; ignoring");
6329                                                    policy = RestorePolicy.IGNORE;
6330                                                }
6331                                            }
6332                                        } else {
6333                                            Slog.w(TAG, "Restore manifest signatures do not match "
6334                                                    + "installed application for " + info.packageName);
6335                                        }
6336                                    } else {
6337                                        Slog.w(TAG, "Package " + info.packageName
6338                                                + " is system level with no agent");
6339                                    }
6340                                } else {
6341                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
6342                                            + info.packageName + " but allowBackup=false");
6343                                }
6344                            } catch (NameNotFoundException e) {
6345                                // Okay, the target app isn't installed.  We can process
6346                                // the restore properly only if the dataset provides the
6347                                // apk file and we can successfully install it.
6348                                if (mAllowApks) {
6349                                    if (DEBUG) Slog.i(TAG, "Package " + info.packageName
6350                                            + " not installed; requiring apk in dataset");
6351                                    policy = RestorePolicy.ACCEPT_IF_APK;
6352                                } else {
6353                                    policy = RestorePolicy.IGNORE;
6354                                }
6355                            }
6356
6357                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
6358                                Slog.i(TAG, "Cannot restore package " + info.packageName
6359                                        + " without the matching .apk");
6360                            }
6361                        } else {
6362                            Slog.i(TAG, "Missing signature on backed-up package "
6363                                    + info.packageName);
6364                        }
6365                    } else {
6366                        Slog.i(TAG, "Expected package " + info.packageName
6367                                + " but restore manifest claims " + manifestPackage);
6368                    }
6369                } else {
6370                    Slog.i(TAG, "Unknown restore manifest version " + version
6371                            + " for package " + info.packageName);
6372                }
6373            } catch (NumberFormatException e) {
6374                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
6375            } catch (IllegalArgumentException e) {
6376                Slog.w(TAG, e.getMessage());
6377            }
6378
6379            return policy;
6380        }
6381
6382        // Builds a line from a byte buffer starting at 'offset', and returns
6383        // the index of the next unconsumed data in the buffer.
6384        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
6385            final int end = buffer.length;
6386            if (offset >= end) throw new IOException("Incomplete data");
6387
6388            int pos;
6389            for (pos = offset; pos < end; pos++) {
6390                byte c = buffer[pos];
6391                // at LF we declare end of line, and return the next char as the
6392                // starting point for the next time through
6393                if (c == '\n') {
6394                    break;
6395                }
6396            }
6397            outStr[0] = new String(buffer, offset, pos - offset);
6398            pos++;  // may be pointing an extra byte past the end but that's okay
6399            return pos;
6400        }
6401
6402        void dumpFileMetadata(FileMetadata info) {
6403            if (MORE_DEBUG) {
6404                StringBuilder b = new StringBuilder(128);
6405
6406                // mode string
6407                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
6408                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
6409                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
6410                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
6411                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
6412                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
6413                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
6414                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
6415                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
6416                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
6417                b.append(String.format(" %9d ", info.size));
6418
6419                Date stamp = new Date(info.mtime);
6420                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
6421
6422                b.append(info.packageName);
6423                b.append(" :: ");
6424                b.append(info.domain);
6425                b.append(" :: ");
6426                b.append(info.path);
6427
6428                Slog.i(TAG, b.toString());
6429            }
6430        }
6431
6432        // Consume a tar file header block [sequence] and accumulate the relevant metadata
6433        FileMetadata readTarHeaders(InputStream instream) throws IOException {
6434            byte[] block = new byte[512];
6435            FileMetadata info = null;
6436
6437            boolean gotHeader = readTarHeader(instream, block);
6438            if (gotHeader) {
6439                try {
6440                    // okay, presume we're okay, and extract the various metadata
6441                    info = new FileMetadata();
6442                    info.size = extractRadix(block, 124, 12, 8);
6443                    info.mtime = extractRadix(block, 136, 12, 8);
6444                    info.mode = extractRadix(block, 100, 8, 8);
6445
6446                    info.path = extractString(block, 345, 155); // prefix
6447                    String path = extractString(block, 0, 100);
6448                    if (path.length() > 0) {
6449                        if (info.path.length() > 0) info.path += '/';
6450                        info.path += path;
6451                    }
6452
6453                    // tar link indicator field: 1 byte at offset 156 in the header.
6454                    int typeChar = block[156];
6455                    if (typeChar == 'x') {
6456                        // pax extended header, so we need to read that
6457                        gotHeader = readPaxExtendedHeader(instream, info);
6458                        if (gotHeader) {
6459                            // and after a pax extended header comes another real header -- read
6460                            // that to find the real file type
6461                            gotHeader = readTarHeader(instream, block);
6462                        }
6463                        if (!gotHeader) throw new IOException("Bad or missing pax header");
6464
6465                        typeChar = block[156];
6466                    }
6467
6468                    switch (typeChar) {
6469                        case '0': info.type = BackupAgent.TYPE_FILE; break;
6470                        case '5': {
6471                            info.type = BackupAgent.TYPE_DIRECTORY;
6472                            if (info.size != 0) {
6473                                Slog.w(TAG, "Directory entry with nonzero size in header");
6474                                info.size = 0;
6475                            }
6476                            break;
6477                        }
6478                        case 0: {
6479                            // presume EOF
6480                            if (MORE_DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
6481                            return null;
6482                        }
6483                        default: {
6484                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
6485                            throw new IOException("Unknown entity type " + typeChar);
6486                        }
6487                    }
6488
6489                    // Parse out the path
6490                    //
6491                    // first: apps/shared/unrecognized
6492                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
6493                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
6494                        // File in shared storage.  !!! TODO: implement this.
6495                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
6496                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
6497                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
6498                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
6499                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
6500                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
6501                        // App content!  Parse out the package name and domain
6502
6503                        // strip the apps/ prefix
6504                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
6505
6506                        // extract the package name
6507                        int slash = info.path.indexOf('/');
6508                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
6509                        info.packageName = info.path.substring(0, slash);
6510                        info.path = info.path.substring(slash+1);
6511
6512                        // if it's a manifest or metadata payload we're done, otherwise parse
6513                        // out the domain into which the file will be restored
6514                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
6515                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
6516                            slash = info.path.indexOf('/');
6517                            if (slash < 0) {
6518                                throw new IOException("Illegal semantic path in non-manifest "
6519                                        + info.path);
6520                            }
6521                            info.domain = info.path.substring(0, slash);
6522                            info.path = info.path.substring(slash + 1);
6523                        }
6524                    }
6525                } catch (IOException e) {
6526                    if (DEBUG) {
6527                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
6528                        if (MORE_DEBUG) {
6529                            HEXLOG(block);
6530                        }
6531                    }
6532                    throw e;
6533                }
6534            }
6535            return info;
6536        }
6537
6538        private boolean isRestorableFile(FileMetadata info) {
6539            if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
6540                if (MORE_DEBUG) {
6541                    Slog.i(TAG, "Dropping cache file path " + info.path);
6542                }
6543                return false;
6544            }
6545
6546            if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
6547                // It's possible this is "no-backup" dir contents in an archive stream
6548                // produced on a device running a version of the OS that predates that
6549                // API.  Respect the no-backup intention and don't let the data get to
6550                // the app.
6551                if (info.path.startsWith("no_backup/")) {
6552                    if (MORE_DEBUG) {
6553                        Slog.i(TAG, "Dropping no_backup file path " + info.path);
6554                    }
6555                    return false;
6556                }
6557            }
6558
6559            // The path needs to be canonical
6560            if (info.path.contains("..") || info.path.contains("//")) {
6561                if (MORE_DEBUG) {
6562                    Slog.w(TAG, "Dropping invalid path " + info.path);
6563                }
6564                return false;
6565            }
6566
6567            // Otherwise we think this file is good to go
6568            return true;
6569        }
6570
6571        private void HEXLOG(byte[] block) {
6572            int offset = 0;
6573            int todo = block.length;
6574            StringBuilder buf = new StringBuilder(64);
6575            while (todo > 0) {
6576                buf.append(String.format("%04x   ", offset));
6577                int numThisLine = (todo > 16) ? 16 : todo;
6578                for (int i = 0; i < numThisLine; i++) {
6579                    buf.append(String.format("%02x ", block[offset+i]));
6580                }
6581                Slog.i("hexdump", buf.toString());
6582                buf.setLength(0);
6583                todo -= numThisLine;
6584                offset += numThisLine;
6585            }
6586        }
6587
6588        // Read exactly the given number of bytes into a buffer at the stated offset.
6589        // Returns false if EOF is encountered before the requested number of bytes
6590        // could be read.
6591        int readExactly(InputStream in, byte[] buffer, int offset, int size)
6592                throws IOException {
6593            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
6594if (MORE_DEBUG) Slog.i(TAG, "  ... readExactly(" + size + ") called");
6595            int soFar = 0;
6596            while (soFar < size) {
6597                int nRead = in.read(buffer, offset + soFar, size - soFar);
6598                if (nRead <= 0) {
6599                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
6600                    break;
6601                }
6602                soFar += nRead;
6603if (MORE_DEBUG) Slog.v(TAG, "   + got " + nRead + "; now wanting " + (size - soFar));
6604            }
6605            return soFar;
6606        }
6607
6608        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
6609            final int got = readExactly(instream, block, 0, 512);
6610            if (got == 0) return false;     // Clean EOF
6611            if (got < 512) throw new IOException("Unable to read full block header");
6612            mBytes += 512;
6613            return true;
6614        }
6615
6616        // overwrites 'info' fields based on the pax extended header
6617        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
6618                throws IOException {
6619            // We should never see a pax extended header larger than this
6620            if (info.size > 32*1024) {
6621                Slog.w(TAG, "Suspiciously large pax header size " + info.size
6622                        + " - aborting");
6623                throw new IOException("Sanity failure: pax header size " + info.size);
6624            }
6625
6626            // read whole blocks, not just the content size
6627            int numBlocks = (int)((info.size + 511) >> 9);
6628            byte[] data = new byte[numBlocks * 512];
6629            if (readExactly(instream, data, 0, data.length) < data.length) {
6630                throw new IOException("Unable to read full pax header");
6631            }
6632            mBytes += data.length;
6633
6634            final int contentSize = (int) info.size;
6635            int offset = 0;
6636            do {
6637                // extract the line at 'offset'
6638                int eol = offset+1;
6639                while (eol < contentSize && data[eol] != ' ') eol++;
6640                if (eol >= contentSize) {
6641                    // error: we just hit EOD looking for the end of the size field
6642                    throw new IOException("Invalid pax data");
6643                }
6644                // eol points to the space between the count and the key
6645                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
6646                int key = eol + 1;  // start of key=value
6647                eol = offset + linelen - 1; // trailing LF
6648                int value;
6649                for (value = key+1; data[value] != '=' && value <= eol; value++);
6650                if (value > eol) {
6651                    throw new IOException("Invalid pax declaration");
6652                }
6653
6654                // pax requires that key/value strings be in UTF-8
6655                String keyStr = new String(data, key, value-key, "UTF-8");
6656                // -1 to strip the trailing LF
6657                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
6658
6659                if ("path".equals(keyStr)) {
6660                    info.path = valStr;
6661                } else if ("size".equals(keyStr)) {
6662                    info.size = Long.parseLong(valStr);
6663                } else {
6664                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
6665                }
6666
6667                offset += linelen;
6668            } while (offset < contentSize);
6669
6670            return true;
6671        }
6672
6673        long extractRadix(byte[] data, int offset, int maxChars, int radix)
6674                throws IOException {
6675            long value = 0;
6676            final int end = offset + maxChars;
6677            for (int i = offset; i < end; i++) {
6678                final byte b = data[i];
6679                // Numeric fields in tar can terminate with either NUL or SPC
6680                if (b == 0 || b == ' ') break;
6681                if (b < '0' || b > ('0' + radix - 1)) {
6682                    throw new IOException("Invalid number in header: '" + (char)b
6683                            + "' for radix " + radix);
6684                }
6685                value = radix * value + (b - '0');
6686            }
6687            return value;
6688        }
6689
6690        String extractString(byte[] data, int offset, int maxChars) throws IOException {
6691            final int end = offset + maxChars;
6692            int eos = offset;
6693            // tar string fields terminate early with a NUL
6694            while (eos < end && data[eos] != 0) eos++;
6695            return new String(data, offset, eos-offset, "US-ASCII");
6696        }
6697
6698        void sendStartRestore() {
6699            if (mObserver != null) {
6700                try {
6701                    mObserver.onStartRestore();
6702                } catch (RemoteException e) {
6703                    Slog.w(TAG, "full restore observer went away: startRestore");
6704                    mObserver = null;
6705                }
6706            }
6707        }
6708
6709        void sendOnRestorePackage(String name) {
6710            if (mObserver != null) {
6711                try {
6712                    // TODO: use a more user-friendly name string
6713                    mObserver.onRestorePackage(name);
6714                } catch (RemoteException e) {
6715                    Slog.w(TAG, "full restore observer went away: restorePackage");
6716                    mObserver = null;
6717                }
6718            }
6719        }
6720
6721        void sendEndRestore() {
6722            if (mObserver != null) {
6723                try {
6724                    mObserver.onEndRestore();
6725                } catch (RemoteException e) {
6726                    Slog.w(TAG, "full restore observer went away: endRestore");
6727                    mObserver = null;
6728                }
6729            }
6730        }
6731    }
6732
6733    // ***** end new engine class ***
6734
6735    // Used for synchronizing doRestoreFinished during adb restore
6736    class AdbRestoreFinishedLatch implements BackupRestoreTask {
6737        static final String TAG = "AdbRestoreFinishedLatch";
6738        final CountDownLatch mLatch;
6739        private final int mCurrentOpToken;
6740
6741        AdbRestoreFinishedLatch(int currentOpToken) {
6742            mLatch = new CountDownLatch(1);
6743            mCurrentOpToken = currentOpToken;
6744        }
6745
6746        void await() {
6747            boolean latched = false;
6748            try {
6749                latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
6750            } catch (InterruptedException e) {
6751                Slog.w(TAG, "Interrupted!");
6752            }
6753        }
6754
6755        @Override
6756        public void execute() {
6757            // Unused
6758        }
6759
6760        @Override
6761        public void operationComplete(long result) {
6762            if (MORE_DEBUG) {
6763                Slog.w(TAG, "adb onRestoreFinished() complete");
6764            }
6765            mLatch.countDown();
6766            removeOperation(mCurrentOpToken);
6767        }
6768
6769        @Override
6770        public void handleCancel(boolean cancelAll) {
6771            if (DEBUG) {
6772                Slog.w(TAG, "adb onRestoreFinished() timed out");
6773            }
6774            mLatch.countDown();
6775            removeOperation(mCurrentOpToken);
6776        }
6777    }
6778
6779    class PerformAdbRestoreTask implements Runnable {
6780        ParcelFileDescriptor mInputFile;
6781        String mCurrentPassword;
6782        String mDecryptPassword;
6783        IFullBackupRestoreObserver mObserver;
6784        AtomicBoolean mLatchObject;
6785        IBackupAgent mAgent;
6786        String mAgentPackage;
6787        ApplicationInfo mTargetApp;
6788        FullBackupObbConnection mObbConnection = null;
6789        ParcelFileDescriptor[] mPipes = null;
6790        byte[] mWidgetData = null;
6791
6792        long mBytes;
6793
6794        // Runner that can be placed on a separate thread to do in-process invocation
6795        // of the "restore finished" API asynchronously.  Used by adb restore.
6796        class RestoreFinishedRunnable implements Runnable {
6797            final IBackupAgent mAgent;
6798            final int mToken;
6799
6800            RestoreFinishedRunnable(IBackupAgent agent, int token) {
6801                mAgent = agent;
6802                mToken = token;
6803            }
6804
6805            @Override
6806            public void run() {
6807                try {
6808                    mAgent.doRestoreFinished(mToken, mBackupManagerBinder);
6809                } catch (RemoteException e) {
6810                    // never happens; this is used only for local binder calls
6811                }
6812            }
6813        }
6814
6815        // possible handling states for a given package in the restore dataset
6816        final HashMap<String, RestorePolicy> mPackagePolicies
6817                = new HashMap<String, RestorePolicy>();
6818
6819        // installer package names for each encountered app, derived from the manifests
6820        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
6821
6822        // Signatures for a given package found in its manifest file
6823        final HashMap<String, Signature[]> mManifestSignatures
6824                = new HashMap<String, Signature[]>();
6825
6826        // Packages we've already wiped data on when restoring their first file
6827        final HashSet<String> mClearedPackages = new HashSet<String>();
6828
6829        PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
6830                IFullBackupRestoreObserver observer, AtomicBoolean latch) {
6831            mInputFile = fd;
6832            mCurrentPassword = curPassword;
6833            mDecryptPassword = decryptPassword;
6834            mObserver = observer;
6835            mLatchObject = latch;
6836            mAgent = null;
6837            mAgentPackage = null;
6838            mTargetApp = null;
6839            mObbConnection = new FullBackupObbConnection();
6840
6841            // Which packages we've already wiped data on.  We prepopulate this
6842            // with a whitelist of packages known to be unclearable.
6843            mClearedPackages.add("android");
6844            mClearedPackages.add(SETTINGS_PACKAGE);
6845        }
6846
6847        class RestoreFileRunnable implements Runnable {
6848            IBackupAgent mAgent;
6849            FileMetadata mInfo;
6850            ParcelFileDescriptor mSocket;
6851            int mToken;
6852
6853            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
6854                    ParcelFileDescriptor socket, int token) throws IOException {
6855                mAgent = agent;
6856                mInfo = info;
6857                mToken = token;
6858
6859                // This class is used strictly for process-local binder invocations.  The
6860                // semantics of ParcelFileDescriptor differ in this case; in particular, we
6861                // do not automatically get a 'dup'ed descriptor that we can can continue
6862                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
6863                // before proceeding to do the restore.
6864                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
6865            }
6866
6867            @Override
6868            public void run() {
6869                try {
6870                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
6871                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
6872                            mToken, mBackupManagerBinder);
6873                } catch (RemoteException e) {
6874                    // never happens; this is used strictly for local binder calls
6875                }
6876            }
6877        }
6878
6879        @Override
6880        public void run() {
6881            Slog.i(TAG, "--- Performing full-dataset restore ---");
6882            mObbConnection.establish();
6883            sendStartRestore();
6884
6885            // Are we able to restore shared-storage data?
6886            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
6887                mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
6888            }
6889
6890            FileInputStream rawInStream = null;
6891            DataInputStream rawDataIn = null;
6892            try {
6893                if (!backupPasswordMatches(mCurrentPassword)) {
6894                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
6895                    return;
6896                }
6897
6898                mBytes = 0;
6899                byte[] buffer = new byte[32 * 1024];
6900                rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
6901                rawDataIn = new DataInputStream(rawInStream);
6902
6903                // First, parse out the unencrypted/uncompressed header
6904                boolean compressed = false;
6905                InputStream preCompressStream = rawInStream;
6906                final InputStream in;
6907
6908                boolean okay = false;
6909                final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
6910                byte[] streamHeader = new byte[headerLen];
6911                rawDataIn.readFully(streamHeader);
6912                byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
6913                if (Arrays.equals(magicBytes, streamHeader)) {
6914                    // okay, header looks good.  now parse out the rest of the fields.
6915                    String s = readHeaderLine(rawInStream);
6916                    final int archiveVersion = Integer.parseInt(s);
6917                    if (archiveVersion <= BACKUP_FILE_VERSION) {
6918                        // okay, it's a version we recognize.  if it's version 1, we may need
6919                        // to try two different PBKDF2 regimes to compare checksums.
6920                        final boolean pbkdf2Fallback = (archiveVersion == 1);
6921
6922                        s = readHeaderLine(rawInStream);
6923                        compressed = (Integer.parseInt(s) != 0);
6924                        s = readHeaderLine(rawInStream);
6925                        if (s.equals("none")) {
6926                            // no more header to parse; we're good to go
6927                            okay = true;
6928                        } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
6929                            preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
6930                                    rawInStream);
6931                            if (preCompressStream != null) {
6932                                okay = true;
6933                            }
6934                        } else Slog.w(TAG, "Archive is encrypted but no password given");
6935                    } else Slog.w(TAG, "Wrong header version: " + s);
6936                } else Slog.w(TAG, "Didn't read the right header magic");
6937
6938                if (!okay) {
6939                    Slog.w(TAG, "Invalid restore data; aborting.");
6940                    return;
6941                }
6942
6943                // okay, use the right stream layer based on compression
6944                in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
6945
6946                boolean didRestore;
6947                do {
6948                    didRestore = restoreOneFile(in, buffer);
6949                } while (didRestore);
6950
6951                if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
6952            } catch (IOException e) {
6953                Slog.e(TAG, "Unable to read restore input");
6954            } finally {
6955                tearDownPipes();
6956                tearDownAgent(mTargetApp, true);
6957
6958                try {
6959                    if (rawDataIn != null) rawDataIn.close();
6960                    if (rawInStream != null) rawInStream.close();
6961                    mInputFile.close();
6962                } catch (IOException e) {
6963                    Slog.w(TAG, "Close of restore data pipe threw", e);
6964                    /* nothing we can do about this */
6965                }
6966                synchronized (mLatchObject) {
6967                    mLatchObject.set(true);
6968                    mLatchObject.notifyAll();
6969                }
6970                mObbConnection.tearDown();
6971                sendEndRestore();
6972                Slog.d(TAG, "Full restore pass complete.");
6973                mWakelock.release();
6974            }
6975        }
6976
6977        String readHeaderLine(InputStream in) throws IOException {
6978            int c;
6979            StringBuilder buffer = new StringBuilder(80);
6980            while ((c = in.read()) >= 0) {
6981                if (c == '\n') break;   // consume and discard the newlines
6982                buffer.append((char)c);
6983            }
6984            return buffer.toString();
6985        }
6986
6987        InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
6988                int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
6989                boolean doLog) {
6990            InputStream result = null;
6991
6992            try {
6993                Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
6994                SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt,
6995                        rounds);
6996                byte[] IV = hexToByteArray(userIvHex);
6997                IvParameterSpec ivSpec = new IvParameterSpec(IV);
6998                c.init(Cipher.DECRYPT_MODE,
6999                        new SecretKeySpec(userKey.getEncoded(), "AES"),
7000                        ivSpec);
7001                byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
7002                byte[] mkBlob = c.doFinal(mkCipher);
7003
7004                // first, the master key IV
7005                int offset = 0;
7006                int len = mkBlob[offset++];
7007                IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
7008                offset += len;
7009                // then the master key itself
7010                len = mkBlob[offset++];
7011                byte[] mk = Arrays.copyOfRange(mkBlob,
7012                        offset, offset + len);
7013                offset += len;
7014                // and finally the master key checksum hash
7015                len = mkBlob[offset++];
7016                byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
7017                        offset, offset + len);
7018
7019                // now validate the decrypted master key against the checksum
7020                byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds);
7021                if (Arrays.equals(calculatedCk, mkChecksum)) {
7022                    ivSpec = new IvParameterSpec(IV);
7023                    c.init(Cipher.DECRYPT_MODE,
7024                            new SecretKeySpec(mk, "AES"),
7025                            ivSpec);
7026                    // Only if all of the above worked properly will 'result' be assigned
7027                    result = new CipherInputStream(rawInStream, c);
7028                } else if (doLog) Slog.w(TAG, "Incorrect password");
7029            } catch (InvalidAlgorithmParameterException e) {
7030                if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e);
7031            } catch (BadPaddingException e) {
7032                // This case frequently occurs when the wrong password is used to decrypt
7033                // the master key.  Use the identical "incorrect password" log text as is
7034                // used in the checksum failure log in order to avoid providing additional
7035                // information to an attacker.
7036                if (doLog) Slog.w(TAG, "Incorrect password");
7037            } catch (IllegalBlockSizeException e) {
7038                if (doLog) Slog.w(TAG, "Invalid block size in master key");
7039            } catch (NoSuchAlgorithmException e) {
7040                if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!");
7041            } catch (NoSuchPaddingException e) {
7042                if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!");
7043            } catch (InvalidKeyException e) {
7044                if (doLog) Slog.w(TAG, "Illegal password; aborting");
7045            }
7046
7047            return result;
7048        }
7049
7050        InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
7051                InputStream rawInStream) {
7052            InputStream result = null;
7053            try {
7054                if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
7055
7056                    String userSaltHex = readHeaderLine(rawInStream); // 5
7057                    byte[] userSalt = hexToByteArray(userSaltHex);
7058
7059                    String ckSaltHex = readHeaderLine(rawInStream); // 6
7060                    byte[] ckSalt = hexToByteArray(ckSaltHex);
7061
7062                    int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
7063                    String userIvHex = readHeaderLine(rawInStream); // 8
7064
7065                    String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
7066
7067                    // decrypt the master key blob
7068                    result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt,
7069                            rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
7070                    if (result == null && pbkdf2Fallback) {
7071                        result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt,
7072                                rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
7073                    }
7074                } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
7075            } catch (NumberFormatException e) {
7076                Slog.w(TAG, "Can't parse restore data header");
7077            } catch (IOException e) {
7078                Slog.w(TAG, "Can't read input header");
7079            }
7080
7081            return result;
7082        }
7083
7084        boolean restoreOneFile(InputStream instream, byte[] buffer) {
7085            FileMetadata info;
7086            try {
7087                info = readTarHeaders(instream);
7088                if (info != null) {
7089                    if (MORE_DEBUG) {
7090                        dumpFileMetadata(info);
7091                    }
7092
7093                    final String pkg = info.packageName;
7094                    if (!pkg.equals(mAgentPackage)) {
7095                        // okay, change in package; set up our various
7096                        // bookkeeping if we haven't seen it yet
7097                        if (!mPackagePolicies.containsKey(pkg)) {
7098                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
7099                        }
7100
7101                        // Clean up the previous agent relationship if necessary,
7102                        // and let the observer know we're considering a new app.
7103                        if (mAgent != null) {
7104                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
7105                            // Now we're really done
7106                            tearDownPipes();
7107                            tearDownAgent(mTargetApp, true);
7108                            mTargetApp = null;
7109                            mAgentPackage = null;
7110                        }
7111                    }
7112
7113                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
7114                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
7115                        mPackageInstallers.put(pkg, info.installerPackageName);
7116                        // We've read only the manifest content itself at this point,
7117                        // so consume the footer before looping around to the next
7118                        // input file
7119                        skipTarPadding(info.size, instream);
7120                        sendOnRestorePackage(pkg);
7121                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
7122                        // Metadata blobs!
7123                        readMetadata(info, instream);
7124                        skipTarPadding(info.size, instream);
7125                    } else {
7126                        // Non-manifest, so it's actual file data.  Is this a package
7127                        // we're ignoring?
7128                        boolean okay = true;
7129                        RestorePolicy policy = mPackagePolicies.get(pkg);
7130                        switch (policy) {
7131                            case IGNORE:
7132                                okay = false;
7133                                break;
7134
7135                            case ACCEPT_IF_APK:
7136                                // If we're in accept-if-apk state, then the first file we
7137                                // see MUST be the apk.
7138                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
7139                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
7140                                    // Try to install the app.
7141                                    String installerName = mPackageInstallers.get(pkg);
7142                                    okay = installApk(info, installerName, instream);
7143                                    // good to go; promote to ACCEPT
7144                                    mPackagePolicies.put(pkg, (okay)
7145                                            ? RestorePolicy.ACCEPT
7146                                            : RestorePolicy.IGNORE);
7147                                    // At this point we've consumed this file entry
7148                                    // ourselves, so just strip the tar footer and
7149                                    // go on to the next file in the input stream
7150                                    skipTarPadding(info.size, instream);
7151                                    return true;
7152                                } else {
7153                                    // File data before (or without) the apk.  We can't
7154                                    // handle it coherently in this case so ignore it.
7155                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
7156                                    okay = false;
7157                                }
7158                                break;
7159
7160                            case ACCEPT:
7161                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
7162                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
7163                                    // we can take the data without the apk, so we
7164                                    // *want* to do so.  skip the apk by declaring this
7165                                    // one file not-okay without changing the restore
7166                                    // policy for the package.
7167                                    okay = false;
7168                                }
7169                                break;
7170
7171                            default:
7172                                // Something has gone dreadfully wrong when determining
7173                                // the restore policy from the manifest.  Ignore the
7174                                // rest of this package's data.
7175                                Slog.e(TAG, "Invalid policy from manifest");
7176                                okay = false;
7177                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
7178                                break;
7179                        }
7180
7181                        // The path needs to be canonical
7182                        if (info.path.contains("..") || info.path.contains("//")) {
7183                            if (MORE_DEBUG) {
7184                                Slog.w(TAG, "Dropping invalid path " + info.path);
7185                            }
7186                            okay = false;
7187                        }
7188
7189                        // If the policy is satisfied, go ahead and set up to pipe the
7190                        // data to the agent.
7191                        if (DEBUG && okay && mAgent != null) {
7192                            Slog.i(TAG, "Reusing existing agent instance");
7193                        }
7194                        if (okay && mAgent == null) {
7195                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
7196
7197                            try {
7198                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
7199
7200                                // If we haven't sent any data to this app yet, we probably
7201                                // need to clear it first.  Check that.
7202                                if (!mClearedPackages.contains(pkg)) {
7203                                    // apps with their own backup agents are
7204                                    // responsible for coherently managing a full
7205                                    // restore.
7206                                    if (mTargetApp.backupAgentName == null) {
7207                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
7208                                        clearApplicationDataSynchronous(pkg);
7209                                    } else {
7210                                        if (DEBUG) Slog.d(TAG, "backup agent ("
7211                                                + mTargetApp.backupAgentName + ") => no clear");
7212                                    }
7213                                    mClearedPackages.add(pkg);
7214                                } else {
7215                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
7216                                }
7217
7218                                // All set; now set up the IPC and launch the agent
7219                                setUpPipes();
7220                                mAgent = bindToAgentSynchronous(mTargetApp,
7221                                        ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL);
7222                                mAgentPackage = pkg;
7223                            } catch (IOException e) {
7224                                // fall through to error handling
7225                            } catch (NameNotFoundException e) {
7226                                // fall through to error handling
7227                            }
7228
7229                            if (mAgent == null) {
7230                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
7231                                okay = false;
7232                                tearDownPipes();
7233                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
7234                            }
7235                        }
7236
7237                        // Sanity check: make sure we never give data to the wrong app.  This
7238                        // should never happen but a little paranoia here won't go amiss.
7239                        if (okay && !pkg.equals(mAgentPackage)) {
7240                            Slog.e(TAG, "Restoring data for " + pkg
7241                                    + " but agent is for " + mAgentPackage);
7242                            okay = false;
7243                        }
7244
7245                        // At this point we have an agent ready to handle the full
7246                        // restore data as well as a pipe for sending data to
7247                        // that agent.  Tell the agent to start reading from the
7248                        // pipe.
7249                        if (okay) {
7250                            boolean agentSuccess = true;
7251                            long toCopy = info.size;
7252                            final int token = generateToken();
7253                            try {
7254                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null,
7255                                        OP_TYPE_RESTORE_WAIT);
7256                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
7257                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
7258                                            + " : " + info.path);
7259                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
7260                                            info.size, info.type, info.path, info.mode,
7261                                            info.mtime, token, mBackupManagerBinder);
7262                                } else {
7263                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
7264                                            + info.path);
7265                                    // fire up the app's agent listening on the socket.  If
7266                                    // the agent is running in the system process we can't
7267                                    // just invoke it asynchronously, so we provide a thread
7268                                    // for it here.
7269                                    if (mTargetApp.processName.equals("system")) {
7270                                        Slog.d(TAG, "system process agent - spinning a thread");
7271                                        RestoreFileRunnable runner = new RestoreFileRunnable(
7272                                                mAgent, info, mPipes[0], token);
7273                                        new Thread(runner, "restore-sys-runner").start();
7274                                    } else {
7275                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
7276                                                info.domain, info.path, info.mode, info.mtime,
7277                                                token, mBackupManagerBinder);
7278                                    }
7279                                }
7280                            } catch (IOException e) {
7281                                // couldn't dup the socket for a process-local restore
7282                                Slog.d(TAG, "Couldn't establish restore");
7283                                agentSuccess = false;
7284                                okay = false;
7285                            } catch (RemoteException e) {
7286                                // whoops, remote entity went away.  We'll eat the content
7287                                // ourselves, then, and not copy it over.
7288                                Slog.e(TAG, "Agent crashed during full restore");
7289                                agentSuccess = false;
7290                                okay = false;
7291                            }
7292
7293                            // Copy over the data if the agent is still good
7294                            if (okay) {
7295                                boolean pipeOkay = true;
7296                                FileOutputStream pipe = new FileOutputStream(
7297                                        mPipes[1].getFileDescriptor());
7298                                while (toCopy > 0) {
7299                                    int toRead = (toCopy > buffer.length)
7300                                    ? buffer.length : (int)toCopy;
7301                                    int nRead = instream.read(buffer, 0, toRead);
7302                                    if (nRead >= 0) mBytes += nRead;
7303                                    if (nRead <= 0) break;
7304                                    toCopy -= nRead;
7305
7306                                    // send it to the output pipe as long as things
7307                                    // are still good
7308                                    if (pipeOkay) {
7309                                        try {
7310                                            pipe.write(buffer, 0, nRead);
7311                                        } catch (IOException e) {
7312                                            Slog.e(TAG, "Failed to write to restore pipe", e);
7313                                            pipeOkay = false;
7314                                        }
7315                                    }
7316                                }
7317
7318                                // done sending that file!  Now we just need to consume
7319                                // the delta from info.size to the end of block.
7320                                skipTarPadding(info.size, instream);
7321
7322                                // and now that we've sent it all, wait for the remote
7323                                // side to acknowledge receipt
7324                                agentSuccess = waitUntilOperationComplete(token);
7325                            }
7326
7327                            // okay, if the remote end failed at any point, deal with
7328                            // it by ignoring the rest of the restore on it
7329                            if (!agentSuccess) {
7330                                if (DEBUG) {
7331                                    Slog.d(TAG, "Agent failure restoring " + pkg + "; now ignoring");
7332                                }
7333                                mBackupHandler.removeMessages(MSG_RESTORE_OPERATION_TIMEOUT);
7334                                tearDownPipes();
7335                                tearDownAgent(mTargetApp, false);
7336                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
7337                            }
7338                        }
7339
7340                        // Problems setting up the agent communication, or an already-
7341                        // ignored package: skip to the next tar stream entry by
7342                        // reading and discarding this file.
7343                        if (!okay) {
7344                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
7345                            long bytesToConsume = (info.size + 511) & ~511;
7346                            while (bytesToConsume > 0) {
7347                                int toRead = (bytesToConsume > buffer.length)
7348                                ? buffer.length : (int)bytesToConsume;
7349                                long nRead = instream.read(buffer, 0, toRead);
7350                                if (nRead >= 0) mBytes += nRead;
7351                                if (nRead <= 0) break;
7352                                bytesToConsume -= nRead;
7353                            }
7354                        }
7355                    }
7356                }
7357            } catch (IOException e) {
7358                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
7359                // treat as EOF
7360                info = null;
7361            }
7362
7363            return (info != null);
7364        }
7365
7366        void setUpPipes() throws IOException {
7367            mPipes = ParcelFileDescriptor.createPipe();
7368        }
7369
7370        void tearDownPipes() {
7371            if (mPipes != null) {
7372                try {
7373                    mPipes[0].close();
7374                    mPipes[0] = null;
7375                    mPipes[1].close();
7376                    mPipes[1] = null;
7377                } catch (IOException e) {
7378                    Slog.w(TAG, "Couldn't close agent pipes", e);
7379                }
7380                mPipes = null;
7381            }
7382        }
7383
7384        void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) {
7385            if (mAgent != null) {
7386                try {
7387                    // In the adb restore case, we do restore-finished here
7388                    if (doRestoreFinished) {
7389                        final int token = generateToken();
7390                        final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(token);
7391                        prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, latch,
7392                                OP_TYPE_RESTORE_WAIT);
7393                        if (mTargetApp.processName.equals("system")) {
7394                            if (MORE_DEBUG) {
7395                                Slog.d(TAG, "system agent - restoreFinished on thread");
7396                            }
7397                            Runnable runner = new RestoreFinishedRunnable(mAgent, token);
7398                            new Thread(runner, "restore-sys-finished-runner").start();
7399                        } else {
7400                            mAgent.doRestoreFinished(token, mBackupManagerBinder);
7401                        }
7402
7403                        latch.await();
7404                    }
7405
7406                    // unbind and tidy up even on timeout or failure, just in case
7407                    mActivityManager.unbindBackupAgent(app);
7408
7409                    // The agent was running with a stub Application object, so shut it down.
7410                    // !!! We hardcode the confirmation UI's package name here rather than use a
7411                    //     manifest flag!  TODO something less direct.
7412                    if (app.uid >= Process.FIRST_APPLICATION_UID
7413                            && !app.packageName.equals("com.android.backupconfirm")) {
7414                        if (DEBUG) Slog.d(TAG, "Killing host process");
7415                        mActivityManager.killApplicationProcess(app.processName, app.uid);
7416                    } else {
7417                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
7418                    }
7419                } catch (RemoteException e) {
7420                    Slog.d(TAG, "Lost app trying to shut down");
7421                }
7422                mAgent = null;
7423            }
7424        }
7425
7426        class RestoreInstallObserver extends PackageInstallObserver {
7427            final AtomicBoolean mDone = new AtomicBoolean();
7428            String mPackageName;
7429            int mResult;
7430
7431            public void reset() {
7432                synchronized (mDone) {
7433                    mDone.set(false);
7434                }
7435            }
7436
7437            public void waitForCompletion() {
7438                synchronized (mDone) {
7439                    while (mDone.get() == false) {
7440                        try {
7441                            mDone.wait();
7442                        } catch (InterruptedException e) { }
7443                    }
7444                }
7445            }
7446
7447            int getResult() {
7448                return mResult;
7449            }
7450
7451            @Override
7452            public void onPackageInstalled(String packageName, int returnCode,
7453                    String msg, Bundle extras) {
7454                synchronized (mDone) {
7455                    mResult = returnCode;
7456                    mPackageName = packageName;
7457                    mDone.set(true);
7458                    mDone.notifyAll();
7459                }
7460            }
7461        }
7462
7463        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
7464            final AtomicBoolean mDone = new AtomicBoolean();
7465            int mResult;
7466
7467            public void reset() {
7468                synchronized (mDone) {
7469                    mDone.set(false);
7470                }
7471            }
7472
7473            public void waitForCompletion() {
7474                synchronized (mDone) {
7475                    while (mDone.get() == false) {
7476                        try {
7477                            mDone.wait();
7478                        } catch (InterruptedException e) { }
7479                    }
7480                }
7481            }
7482
7483            @Override
7484            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
7485                synchronized (mDone) {
7486                    mResult = returnCode;
7487                    mDone.set(true);
7488                    mDone.notifyAll();
7489                }
7490            }
7491        }
7492
7493        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
7494        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
7495
7496        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
7497            boolean okay = true;
7498
7499            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
7500
7501            // The file content is an .apk file.  Copy it out to a staging location and
7502            // attempt to install it.
7503            File apkFile = new File(mDataDir, info.packageName);
7504            try {
7505                FileOutputStream apkStream = new FileOutputStream(apkFile);
7506                byte[] buffer = new byte[32 * 1024];
7507                long size = info.size;
7508                while (size > 0) {
7509                    long toRead = (buffer.length < size) ? buffer.length : size;
7510                    int didRead = instream.read(buffer, 0, (int)toRead);
7511                    if (didRead >= 0) mBytes += didRead;
7512                    apkStream.write(buffer, 0, didRead);
7513                    size -= didRead;
7514                }
7515                apkStream.close();
7516
7517                // make sure the installer can read it
7518                apkFile.setReadable(true, false);
7519
7520                // Now install it
7521                Uri packageUri = Uri.fromFile(apkFile);
7522                mInstallObserver.reset();
7523                mPackageManager.installPackage(packageUri, mInstallObserver,
7524                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
7525                        installerPackage);
7526                mInstallObserver.waitForCompletion();
7527
7528                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
7529                    // The only time we continue to accept install of data even if the
7530                    // apk install failed is if we had already determined that we could
7531                    // accept the data regardless.
7532                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
7533                        okay = false;
7534                    }
7535                } else {
7536                    // Okay, the install succeeded.  Make sure it was the right app.
7537                    boolean uninstall = false;
7538                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
7539                        Slog.w(TAG, "Restore stream claimed to include apk for "
7540                                + info.packageName + " but apk was really "
7541                                + mInstallObserver.mPackageName);
7542                        // delete the package we just put in place; it might be fraudulent
7543                        okay = false;
7544                        uninstall = true;
7545                    } else {
7546                        try {
7547                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
7548                                    PackageManager.GET_SIGNATURES);
7549                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
7550                                Slog.w(TAG, "Restore stream contains apk of package "
7551                                        + info.packageName + " but it disallows backup/restore");
7552                                okay = false;
7553                            } else {
7554                                // So far so good -- do the signatures match the manifest?
7555                                Signature[] sigs = mManifestSignatures.get(info.packageName);
7556                                if (signaturesMatch(sigs, pkg)) {
7557                                    // If this is a system-uid app without a declared backup agent,
7558                                    // don't restore any of the file data.
7559                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
7560                                            && (pkg.applicationInfo.backupAgentName == null)) {
7561                                        Slog.w(TAG, "Installed app " + info.packageName
7562                                                + " has restricted uid and no agent");
7563                                        okay = false;
7564                                    }
7565                                } else {
7566                                    Slog.w(TAG, "Installed app " + info.packageName
7567                                            + " signatures do not match restore manifest");
7568                                    okay = false;
7569                                    uninstall = true;
7570                                }
7571                            }
7572                        } catch (NameNotFoundException e) {
7573                            Slog.w(TAG, "Install of package " + info.packageName
7574                                    + " succeeded but now not found");
7575                            okay = false;
7576                        }
7577                    }
7578
7579                    // If we're not okay at this point, we need to delete the package
7580                    // that we just installed.
7581                    if (uninstall) {
7582                        mDeleteObserver.reset();
7583                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
7584                                mDeleteObserver, 0);
7585                        mDeleteObserver.waitForCompletion();
7586                    }
7587                }
7588            } catch (IOException e) {
7589                Slog.e(TAG, "Unable to transcribe restored apk for install");
7590                okay = false;
7591            } finally {
7592                apkFile.delete();
7593            }
7594
7595            return okay;
7596        }
7597
7598        // Given an actual file content size, consume the post-content padding mandated
7599        // by the tar format.
7600        void skipTarPadding(long size, InputStream instream) throws IOException {
7601            long partial = (size + 512) % 512;
7602            if (partial > 0) {
7603                final int needed = 512 - (int)partial;
7604                byte[] buffer = new byte[needed];
7605                if (readExactly(instream, buffer, 0, needed) == needed) {
7606                    mBytes += needed;
7607                } else throw new IOException("Unexpected EOF in padding");
7608            }
7609        }
7610
7611        // Read a widget metadata file, returning the restored blob
7612        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
7613            // Fail on suspiciously large widget dump files
7614            if (info.size > 64 * 1024) {
7615                throw new IOException("Metadata too big; corrupt? size=" + info.size);
7616            }
7617
7618            byte[] buffer = new byte[(int) info.size];
7619            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
7620                mBytes += info.size;
7621            } else throw new IOException("Unexpected EOF in widget data");
7622
7623            String[] str = new String[1];
7624            int offset = extractLine(buffer, 0, str);
7625            int version = Integer.parseInt(str[0]);
7626            if (version == BACKUP_MANIFEST_VERSION) {
7627                offset = extractLine(buffer, offset, str);
7628                final String pkg = str[0];
7629                if (info.packageName.equals(pkg)) {
7630                    // Data checks out -- the rest of the buffer is a concatenation of
7631                    // binary blobs as described in the comment at writeAppWidgetData()
7632                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
7633                            offset, buffer.length - offset);
7634                    DataInputStream in = new DataInputStream(bin);
7635                    while (bin.available() > 0) {
7636                        int token = in.readInt();
7637                        int size = in.readInt();
7638                        if (size > 64 * 1024) {
7639                            throw new IOException("Datum "
7640                                    + Integer.toHexString(token)
7641                                    + " too big; corrupt? size=" + info.size);
7642                        }
7643                        switch (token) {
7644                            case BACKUP_WIDGET_METADATA_TOKEN:
7645                            {
7646                                if (MORE_DEBUG) {
7647                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
7648                                }
7649                                mWidgetData = new byte[size];
7650                                in.read(mWidgetData);
7651                                break;
7652                            }
7653                            default:
7654                            {
7655                                if (DEBUG) {
7656                                    Slog.i(TAG, "Ignoring metadata blob "
7657                                            + Integer.toHexString(token)
7658                                            + " for " + info.packageName);
7659                                }
7660                                in.skipBytes(size);
7661                                break;
7662                            }
7663                        }
7664                    }
7665                } else {
7666                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
7667                            + " but widget data for " + pkg);
7668                }
7669            } else {
7670                Slog.w(TAG, "Unsupported metadata version " + version);
7671            }
7672        }
7673
7674        // Returns a policy constant; takes a buffer arg to reduce memory churn
7675        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
7676                throws IOException {
7677            // Fail on suspiciously large manifest files
7678            if (info.size > 64 * 1024) {
7679                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
7680            }
7681
7682            byte[] buffer = new byte[(int) info.size];
7683            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
7684                mBytes += info.size;
7685            } else throw new IOException("Unexpected EOF in manifest");
7686
7687            RestorePolicy policy = RestorePolicy.IGNORE;
7688            String[] str = new String[1];
7689            int offset = 0;
7690
7691            try {
7692                offset = extractLine(buffer, offset, str);
7693                int version = Integer.parseInt(str[0]);
7694                if (version == BACKUP_MANIFEST_VERSION) {
7695                    offset = extractLine(buffer, offset, str);
7696                    String manifestPackage = str[0];
7697                    // TODO: handle <original-package>
7698                    if (manifestPackage.equals(info.packageName)) {
7699                        offset = extractLine(buffer, offset, str);
7700                        version = Integer.parseInt(str[0]);  // app version
7701                        offset = extractLine(buffer, offset, str);
7702                        // This is the platform version, which we don't use, but we parse it
7703                        // as a safety against corruption in the manifest.
7704                        Integer.parseInt(str[0]);
7705                        offset = extractLine(buffer, offset, str);
7706                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
7707                        offset = extractLine(buffer, offset, str);
7708                        boolean hasApk = str[0].equals("1");
7709                        offset = extractLine(buffer, offset, str);
7710                        int numSigs = Integer.parseInt(str[0]);
7711                        if (numSigs > 0) {
7712                            Signature[] sigs = new Signature[numSigs];
7713                            for (int i = 0; i < numSigs; i++) {
7714                                offset = extractLine(buffer, offset, str);
7715                                sigs[i] = new Signature(str[0]);
7716                            }
7717                            mManifestSignatures.put(info.packageName, sigs);
7718
7719                            // Okay, got the manifest info we need...
7720                            try {
7721                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
7722                                        info.packageName, PackageManager.GET_SIGNATURES);
7723                                // Fall through to IGNORE if the app explicitly disallows backup
7724                                final int flags = pkgInfo.applicationInfo.flags;
7725                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
7726                                    // Restore system-uid-space packages only if they have
7727                                    // defined a custom backup agent
7728                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
7729                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
7730                                        // Verify signatures against any installed version; if they
7731                                        // don't match, then we fall though and ignore the data.  The
7732                                        // signatureMatch() method explicitly ignores the signature
7733                                        // check for packages installed on the system partition, because
7734                                        // such packages are signed with the platform cert instead of
7735                                        // the app developer's cert, so they're different on every
7736                                        // device.
7737                                        if (signaturesMatch(sigs, pkgInfo)) {
7738                                            if ((pkgInfo.applicationInfo.flags
7739                                                    & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
7740                                                Slog.i(TAG, "Package has restoreAnyVersion; taking data");
7741                                                policy = RestorePolicy.ACCEPT;
7742                                            } else if (pkgInfo.versionCode >= version) {
7743                                                Slog.i(TAG, "Sig + version match; taking data");
7744                                                policy = RestorePolicy.ACCEPT;
7745                                            } else {
7746                                                // The data is from a newer version of the app than
7747                                                // is presently installed.  That means we can only
7748                                                // use it if the matching apk is also supplied.
7749                                                Slog.d(TAG, "Data version " + version
7750                                                        + " is newer than installed version "
7751                                                        + pkgInfo.versionCode + " - requiring apk");
7752                                                policy = RestorePolicy.ACCEPT_IF_APK;
7753                                            }
7754                                        } else {
7755                                            Slog.w(TAG, "Restore manifest signatures do not match "
7756                                                    + "installed application for " + info.packageName);
7757                                        }
7758                                    } else {
7759                                        Slog.w(TAG, "Package " + info.packageName
7760                                                + " is system level with no agent");
7761                                    }
7762                                } else {
7763                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
7764                                            + info.packageName + " but allowBackup=false");
7765                                }
7766                            } catch (NameNotFoundException e) {
7767                                // Okay, the target app isn't installed.  We can process
7768                                // the restore properly only if the dataset provides the
7769                                // apk file and we can successfully install it.
7770                                if (DEBUG) Slog.i(TAG, "Package " + info.packageName
7771                                        + " not installed; requiring apk in dataset");
7772                                policy = RestorePolicy.ACCEPT_IF_APK;
7773                            }
7774
7775                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
7776                                Slog.i(TAG, "Cannot restore package " + info.packageName
7777                                        + " without the matching .apk");
7778                            }
7779                        } else {
7780                            Slog.i(TAG, "Missing signature on backed-up package "
7781                                    + info.packageName);
7782                        }
7783                    } else {
7784                        Slog.i(TAG, "Expected package " + info.packageName
7785                                + " but restore manifest claims " + manifestPackage);
7786                    }
7787                } else {
7788                    Slog.i(TAG, "Unknown restore manifest version " + version
7789                            + " for package " + info.packageName);
7790                }
7791            } catch (NumberFormatException e) {
7792                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
7793            } catch (IllegalArgumentException e) {
7794                Slog.w(TAG, e.getMessage());
7795            }
7796
7797            return policy;
7798        }
7799
7800        // Builds a line from a byte buffer starting at 'offset', and returns
7801        // the index of the next unconsumed data in the buffer.
7802        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
7803            final int end = buffer.length;
7804            if (offset >= end) throw new IOException("Incomplete data");
7805
7806            int pos;
7807            for (pos = offset; pos < end; pos++) {
7808                byte c = buffer[pos];
7809                // at LF we declare end of line, and return the next char as the
7810                // starting point for the next time through
7811                if (c == '\n') {
7812                    break;
7813                }
7814            }
7815            outStr[0] = new String(buffer, offset, pos - offset);
7816            pos++;  // may be pointing an extra byte past the end but that's okay
7817            return pos;
7818        }
7819
7820        void dumpFileMetadata(FileMetadata info) {
7821            if (DEBUG) {
7822                StringBuilder b = new StringBuilder(128);
7823
7824                // mode string
7825                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
7826                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
7827                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
7828                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
7829                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
7830                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
7831                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
7832                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
7833                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
7834                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
7835                b.append(String.format(" %9d ", info.size));
7836
7837                Date stamp = new Date(info.mtime);
7838                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
7839
7840                b.append(info.packageName);
7841                b.append(" :: ");
7842                b.append(info.domain);
7843                b.append(" :: ");
7844                b.append(info.path);
7845
7846                Slog.i(TAG, b.toString());
7847            }
7848        }
7849        // Consume a tar file header block [sequence] and accumulate the relevant metadata
7850        FileMetadata readTarHeaders(InputStream instream) throws IOException {
7851            byte[] block = new byte[512];
7852            FileMetadata info = null;
7853
7854            boolean gotHeader = readTarHeader(instream, block);
7855            if (gotHeader) {
7856                try {
7857                    // okay, presume we're okay, and extract the various metadata
7858                    info = new FileMetadata();
7859                    info.size = extractRadix(block, 124, 12, 8);
7860                    info.mtime = extractRadix(block, 136, 12, 8);
7861                    info.mode = extractRadix(block, 100, 8, 8);
7862
7863                    info.path = extractString(block, 345, 155); // prefix
7864                    String path = extractString(block, 0, 100);
7865                    if (path.length() > 0) {
7866                        if (info.path.length() > 0) info.path += '/';
7867                        info.path += path;
7868                    }
7869
7870                    // tar link indicator field: 1 byte at offset 156 in the header.
7871                    int typeChar = block[156];
7872                    if (typeChar == 'x') {
7873                        // pax extended header, so we need to read that
7874                        gotHeader = readPaxExtendedHeader(instream, info);
7875                        if (gotHeader) {
7876                            // and after a pax extended header comes another real header -- read
7877                            // that to find the real file type
7878                            gotHeader = readTarHeader(instream, block);
7879                        }
7880                        if (!gotHeader) throw new IOException("Bad or missing pax header");
7881
7882                        typeChar = block[156];
7883                    }
7884
7885                    switch (typeChar) {
7886                        case '0': info.type = BackupAgent.TYPE_FILE; break;
7887                        case '5': {
7888                            info.type = BackupAgent.TYPE_DIRECTORY;
7889                            if (info.size != 0) {
7890                                Slog.w(TAG, "Directory entry with nonzero size in header");
7891                                info.size = 0;
7892                            }
7893                            break;
7894                        }
7895                        case 0: {
7896                            // presume EOF
7897                            if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
7898                            return null;
7899                        }
7900                        default: {
7901                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
7902                            throw new IOException("Unknown entity type " + typeChar);
7903                        }
7904                    }
7905
7906                    // Parse out the path
7907                    //
7908                    // first: apps/shared/unrecognized
7909                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
7910                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
7911                        // File in shared storage.  !!! TODO: implement this.
7912                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
7913                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
7914                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
7915                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
7916                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
7917                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
7918                        // App content!  Parse out the package name and domain
7919
7920                        // strip the apps/ prefix
7921                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
7922
7923                        // extract the package name
7924                        int slash = info.path.indexOf('/');
7925                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
7926                        info.packageName = info.path.substring(0, slash);
7927                        info.path = info.path.substring(slash+1);
7928
7929                        // if it's a manifest or metadata payload we're done, otherwise parse
7930                        // out the domain into which the file will be restored
7931                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
7932                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
7933                            slash = info.path.indexOf('/');
7934                            if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
7935                            info.domain = info.path.substring(0, slash);
7936                            info.path = info.path.substring(slash + 1);
7937                        }
7938                    }
7939                } catch (IOException e) {
7940                    if (DEBUG) {
7941                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
7942                        HEXLOG(block);
7943                    }
7944                    throw e;
7945                }
7946            }
7947            return info;
7948        }
7949
7950        private void HEXLOG(byte[] block) {
7951            int offset = 0;
7952            int todo = block.length;
7953            StringBuilder buf = new StringBuilder(64);
7954            while (todo > 0) {
7955                buf.append(String.format("%04x   ", offset));
7956                int numThisLine = (todo > 16) ? 16 : todo;
7957                for (int i = 0; i < numThisLine; i++) {
7958                    buf.append(String.format("%02x ", block[offset+i]));
7959                }
7960                Slog.i("hexdump", buf.toString());
7961                buf.setLength(0);
7962                todo -= numThisLine;
7963                offset += numThisLine;
7964            }
7965        }
7966
7967        // Read exactly the given number of bytes into a buffer at the stated offset.
7968        // Returns false if EOF is encountered before the requested number of bytes
7969        // could be read.
7970        int readExactly(InputStream in, byte[] buffer, int offset, int size)
7971                throws IOException {
7972            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
7973
7974            int soFar = 0;
7975            while (soFar < size) {
7976                int nRead = in.read(buffer, offset + soFar, size - soFar);
7977                if (nRead <= 0) {
7978                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
7979                    break;
7980                }
7981                soFar += nRead;
7982            }
7983            return soFar;
7984        }
7985
7986        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
7987            final int got = readExactly(instream, block, 0, 512);
7988            if (got == 0) return false;     // Clean EOF
7989            if (got < 512) throw new IOException("Unable to read full block header");
7990            mBytes += 512;
7991            return true;
7992        }
7993
7994        // overwrites 'info' fields based on the pax extended header
7995        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
7996                throws IOException {
7997            // We should never see a pax extended header larger than this
7998            if (info.size > 32*1024) {
7999                Slog.w(TAG, "Suspiciously large pax header size " + info.size
8000                        + " - aborting");
8001                throw new IOException("Sanity failure: pax header size " + info.size);
8002            }
8003
8004            // read whole blocks, not just the content size
8005            int numBlocks = (int)((info.size + 511) >> 9);
8006            byte[] data = new byte[numBlocks * 512];
8007            if (readExactly(instream, data, 0, data.length) < data.length) {
8008                throw new IOException("Unable to read full pax header");
8009            }
8010            mBytes += data.length;
8011
8012            final int contentSize = (int) info.size;
8013            int offset = 0;
8014            do {
8015                // extract the line at 'offset'
8016                int eol = offset+1;
8017                while (eol < contentSize && data[eol] != ' ') eol++;
8018                if (eol >= contentSize) {
8019                    // error: we just hit EOD looking for the end of the size field
8020                    throw new IOException("Invalid pax data");
8021                }
8022                // eol points to the space between the count and the key
8023                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
8024                int key = eol + 1;  // start of key=value
8025                eol = offset + linelen - 1; // trailing LF
8026                int value;
8027                for (value = key+1; data[value] != '=' && value <= eol; value++);
8028                if (value > eol) {
8029                    throw new IOException("Invalid pax declaration");
8030                }
8031
8032                // pax requires that key/value strings be in UTF-8
8033                String keyStr = new String(data, key, value-key, "UTF-8");
8034                // -1 to strip the trailing LF
8035                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
8036
8037                if ("path".equals(keyStr)) {
8038                    info.path = valStr;
8039                } else if ("size".equals(keyStr)) {
8040                    info.size = Long.parseLong(valStr);
8041                } else {
8042                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
8043                }
8044
8045                offset += linelen;
8046            } while (offset < contentSize);
8047
8048            return true;
8049        }
8050
8051        long extractRadix(byte[] data, int offset, int maxChars, int radix)
8052                throws IOException {
8053            long value = 0;
8054            final int end = offset + maxChars;
8055            for (int i = offset; i < end; i++) {
8056                final byte b = data[i];
8057                // Numeric fields in tar can terminate with either NUL or SPC
8058                if (b == 0 || b == ' ') break;
8059                if (b < '0' || b > ('0' + radix - 1)) {
8060                    throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix);
8061                }
8062                value = radix * value + (b - '0');
8063            }
8064            return value;
8065        }
8066
8067        String extractString(byte[] data, int offset, int maxChars) throws IOException {
8068            final int end = offset + maxChars;
8069            int eos = offset;
8070            // tar string fields terminate early with a NUL
8071            while (eos < end && data[eos] != 0) eos++;
8072            return new String(data, offset, eos-offset, "US-ASCII");
8073        }
8074
8075        void sendStartRestore() {
8076            if (mObserver != null) {
8077                try {
8078                    mObserver.onStartRestore();
8079                } catch (RemoteException e) {
8080                    Slog.w(TAG, "full restore observer went away: startRestore");
8081                    mObserver = null;
8082                }
8083            }
8084        }
8085
8086        void sendOnRestorePackage(String name) {
8087            if (mObserver != null) {
8088                try {
8089                    // TODO: use a more user-friendly name string
8090                    mObserver.onRestorePackage(name);
8091                } catch (RemoteException e) {
8092                    Slog.w(TAG, "full restore observer went away: restorePackage");
8093                    mObserver = null;
8094                }
8095            }
8096        }
8097
8098        void sendEndRestore() {
8099            if (mObserver != null) {
8100                try {
8101                    mObserver.onEndRestore();
8102                } catch (RemoteException e) {
8103                    Slog.w(TAG, "full restore observer went away: endRestore");
8104                    mObserver = null;
8105                }
8106            }
8107        }
8108    }
8109
8110    // ----- Restore handling -----
8111
8112    // Old style: directly match the stored vs on device signature blocks
8113    static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
8114        if (target == null) {
8115            return false;
8116        }
8117
8118        // If the target resides on the system partition, we allow it to restore
8119        // data from the like-named package in a restore set even if the signatures
8120        // do not match.  (Unlike general applications, those flashed to the system
8121        // partition will be signed with the device's platform certificate, so on
8122        // different phones the same system app will have different signatures.)
8123        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
8124            if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
8125            return true;
8126        }
8127
8128        // Allow unsigned apps, but not signed on one device and unsigned on the other
8129        // !!! TODO: is this the right policy?
8130        Signature[] deviceSigs = target.signatures;
8131        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
8132                + " device=" + deviceSigs);
8133        if ((storedSigs == null || storedSigs.length == 0)
8134                && (deviceSigs == null || deviceSigs.length == 0)) {
8135            return true;
8136        }
8137        if (storedSigs == null || deviceSigs == null) {
8138            return false;
8139        }
8140
8141        // !!! TODO: this demands that every stored signature match one
8142        // that is present on device, and does not demand the converse.
8143        // Is this this right policy?
8144        int nStored = storedSigs.length;
8145        int nDevice = deviceSigs.length;
8146
8147        for (int i=0; i < nStored; i++) {
8148            boolean match = false;
8149            for (int j=0; j < nDevice; j++) {
8150                if (storedSigs[i].equals(deviceSigs[j])) {
8151                    match = true;
8152                    break;
8153                }
8154            }
8155            if (!match) {
8156                return false;
8157            }
8158        }
8159        return true;
8160    }
8161
8162    // Used by both incremental and full restore
8163    void restoreWidgetData(String packageName, byte[] widgetData) {
8164        // Apply the restored widget state and generate the ID update for the app
8165        // TODO: http://b/22388012
8166        if (MORE_DEBUG) {
8167            Slog.i(TAG, "Incorporating restored widget data");
8168        }
8169        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
8170    }
8171
8172    // *****************************
8173    // NEW UNIFIED RESTORE IMPLEMENTATION
8174    // *****************************
8175
8176    // states of the unified-restore state machine
8177    enum UnifiedRestoreState {
8178        INITIAL,
8179        RUNNING_QUEUE,
8180        RESTORE_KEYVALUE,
8181        RESTORE_FULL,
8182        RESTORE_FINISHED,
8183        FINAL
8184    }
8185
8186    class PerformUnifiedRestoreTask implements BackupRestoreTask {
8187        // Transport we're working with to do the restore
8188        private IBackupTransport mTransport;
8189
8190        // Where per-transport saved state goes
8191        File mStateDir;
8192
8193        // Restore observer; may be null
8194        private IRestoreObserver mObserver;
8195
8196        // BackuoManagerMonitor; may be null
8197        private IBackupManagerMonitor mMonitor;
8198
8199        // Token identifying the dataset to the transport
8200        private long mToken;
8201
8202        // When this is a restore-during-install, this is the token identifying the
8203        // operation to the Package Manager, and we must ensure that we let it know
8204        // when we're finished.
8205        private int mPmToken;
8206
8207        // When this is restore-during-install, we need to tell the package manager
8208        // whether we actually launched the app, because this affects notifications
8209        // around externally-visible state transitions.
8210        private boolean mDidLaunch;
8211
8212        // Is this a whole-system restore, i.e. are we establishing a new ancestral
8213        // dataset to base future restore-at-install operations from?
8214        private boolean mIsSystemRestore;
8215
8216        // If this is a single-package restore, what package are we interested in?
8217        private PackageInfo mTargetPackage;
8218
8219        // In all cases, the calculated list of packages that we are trying to restore
8220        private List<PackageInfo> mAcceptSet;
8221
8222        // Our bookkeeping about the ancestral dataset
8223        private PackageManagerBackupAgent mPmAgent;
8224
8225        // Currently-bound backup agent for restore + restoreFinished purposes
8226        private IBackupAgent mAgent;
8227
8228        // What sort of restore we're doing now
8229        private RestoreDescription mRestoreDescription;
8230
8231        // The package we're currently restoring
8232        private PackageInfo mCurrentPackage;
8233
8234        // Widget-related data handled as part of this restore operation
8235        private byte[] mWidgetData;
8236
8237        // Number of apps restored in this pass
8238        private int mCount;
8239
8240        // When did we start?
8241        private long mStartRealtime;
8242
8243        // State machine progress
8244        private UnifiedRestoreState mState;
8245
8246        // How are things going?
8247        private int mStatus;
8248
8249        // Done?
8250        private boolean mFinished;
8251
8252        // Key/value: bookkeeping about staged data and files for agent access
8253        private File mBackupDataName;
8254        private File mStageName;
8255        private File mSavedStateName;
8256        private File mNewStateName;
8257        ParcelFileDescriptor mBackupData;
8258        ParcelFileDescriptor mNewState;
8259
8260        private final int mEphemeralOpToken;
8261
8262        // Invariant: mWakelock is already held, and this task is responsible for
8263        // releasing it at the end of the restore operation.
8264        PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer,
8265                IBackupManagerMonitor monitor, long restoreSetToken, PackageInfo targetPackage,
8266                int pmToken, boolean isFullSystemRestore, String[] filterSet) {
8267            mEphemeralOpToken = generateToken();
8268            mState = UnifiedRestoreState.INITIAL;
8269            mStartRealtime = SystemClock.elapsedRealtime();
8270
8271            mTransport = transport;
8272            mObserver = observer;
8273            mMonitor = monitor;
8274            mToken = restoreSetToken;
8275            mPmToken = pmToken;
8276            mTargetPackage = targetPackage;
8277            mIsSystemRestore = isFullSystemRestore;
8278            mFinished = false;
8279            mDidLaunch = false;
8280
8281            if (targetPackage != null) {
8282                // Single package restore
8283                mAcceptSet = new ArrayList<PackageInfo>();
8284                mAcceptSet.add(targetPackage);
8285            } else {
8286                // Everything possible, or a target set
8287                if (filterSet == null) {
8288                    // We want everything and a pony
8289                    List<PackageInfo> apps =
8290                            PackageManagerBackupAgent.getStorableApplications(mPackageManager);
8291                    filterSet = packagesToNames(apps);
8292                    if (DEBUG) {
8293                        Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
8294                    }
8295                }
8296
8297                mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
8298
8299                // Pro tem, we insist on moving the settings provider package to last place.
8300                // Keep track of whether it's in the list, and bump it down if so.  We also
8301                // want to do the system package itself first if it's called for.
8302                boolean hasSystem = false;
8303                boolean hasSettings = false;
8304                for (int i = 0; i < filterSet.length; i++) {
8305                    try {
8306                        PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
8307                        if ("android".equals(info.packageName)) {
8308                            hasSystem = true;
8309                            continue;
8310                        }
8311                        if (SETTINGS_PACKAGE.equals(info.packageName)) {
8312                            hasSettings = true;
8313                            continue;
8314                        }
8315
8316                        if (appIsEligibleForBackup(info.applicationInfo)) {
8317                            mAcceptSet.add(info);
8318                        }
8319                    } catch (NameNotFoundException e) {
8320                        // requested package name doesn't exist; ignore it
8321                    }
8322                }
8323                if (hasSystem) {
8324                    try {
8325                        mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
8326                    } catch (NameNotFoundException e) {
8327                        // won't happen; we know a priori that it's valid
8328                    }
8329                }
8330                if (hasSettings) {
8331                    try {
8332                        mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
8333                    } catch (NameNotFoundException e) {
8334                        // this one is always valid too
8335                    }
8336                }
8337            }
8338
8339            if (MORE_DEBUG) {
8340                Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
8341                for (PackageInfo info : mAcceptSet) {
8342                    Slog.v(TAG, "   " + info.packageName);
8343                }
8344            }
8345        }
8346
8347        private String[] packagesToNames(List<PackageInfo> apps) {
8348            final int N = apps.size();
8349            String[] names = new String[N];
8350            for (int i = 0; i < N; i++) {
8351                names[i] = apps.get(i).packageName;
8352            }
8353            return names;
8354        }
8355
8356        // Execute one tick of whatever state machine the task implements
8357        @Override
8358        public void execute() {
8359            if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState);
8360            switch (mState) {
8361                case INITIAL:
8362                    startRestore();
8363                    break;
8364
8365                case RUNNING_QUEUE:
8366                    dispatchNextRestore();
8367                    break;
8368
8369                case RESTORE_KEYVALUE:
8370                    restoreKeyValue();
8371                    break;
8372
8373                case RESTORE_FULL:
8374                    restoreFull();
8375                    break;
8376
8377                case RESTORE_FINISHED:
8378                    restoreFinished();
8379                    break;
8380
8381                case FINAL:
8382                    if (!mFinished) finalizeRestore();
8383                    else {
8384                        Slog.e(TAG, "Duplicate finish");
8385                    }
8386                    mFinished = true;
8387                    break;
8388            }
8389        }
8390
8391        /*
8392         * SKETCH OF OPERATION
8393         *
8394         * create one of these PerformUnifiedRestoreTask objects, telling it which
8395         * dataset & transport to address, and then parameters within the restore
8396         * operation: single target package vs many, etc.
8397         *
8398         * 1. transport.startRestore(token, list-of-packages).  If we need @pm@  it is
8399         * always placed first and the settings provider always placed last [for now].
8400         *
8401         * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline]
8402         *
8403         *   [ state change => RUNNING_QUEUE ]
8404         *
8405         * NOW ITERATE:
8406         *
8407         * { 3. t.nextRestorePackage()
8408         *   4. does the metadata for this package allow us to restore it?
8409         *      does the on-disk app permit us to restore it? [re-check allowBackup etc]
8410         *   5. is this a key/value dataset?  => key/value agent restore
8411         *       [ state change => RESTORE_KEYVALUE ]
8412         *       5a. spin up agent
8413         *       5b. t.getRestoreData() to stage it properly
8414         *       5c. call into agent to perform restore
8415         *       5d. tear down agent
8416         *       [ state change => RUNNING_QUEUE ]
8417         *
8418         *   6. else it's a stream dataset:
8419         *       [ state change => RESTORE_FULL ]
8420         *       6a. instantiate the engine for a stream restore: engine handles agent lifecycles
8421         *       6b. spin off engine runner on separate thread
8422         *       6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket
8423         *       [ state change => RUNNING_QUEUE ]
8424         * }
8425         *
8426         *   [ state change => FINAL ]
8427         *
8428         * 7. t.finishRestore(), release wakelock, etc.
8429         *
8430         *
8431         */
8432
8433        // state INITIAL : set up for the restore and read the metadata if necessary
8434        private  void startRestore() {
8435            sendStartRestore(mAcceptSet.size());
8436
8437            // If we're starting a full-system restore, set up to begin widget ID remapping
8438            if (mIsSystemRestore) {
8439                // TODO: http://b/22388012
8440                AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM);
8441            }
8442
8443            try {
8444                String transportDir = mTransport.transportDirName();
8445                mStateDir = new File(mBaseStateDir, transportDir);
8446
8447                // Fetch the current metadata from the dataset first
8448                PackageInfo pmPackage = new PackageInfo();
8449                pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
8450                mAcceptSet.add(0, pmPackage);
8451
8452                PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
8453                mStatus = mTransport.startRestore(mToken, packages);
8454                if (mStatus != BackupTransport.TRANSPORT_OK) {
8455                    Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
8456                    mStatus = BackupTransport.TRANSPORT_ERROR;
8457                    executeNextState(UnifiedRestoreState.FINAL);
8458                    return;
8459                }
8460
8461                RestoreDescription desc = mTransport.nextRestorePackage();
8462                if (desc == null) {
8463                    Slog.e(TAG, "No restore metadata available; halting");
8464                    mStatus = BackupTransport.TRANSPORT_ERROR;
8465                    executeNextState(UnifiedRestoreState.FINAL);
8466                    return;
8467                }
8468                if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
8469                    Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
8470                    mStatus = BackupTransport.TRANSPORT_ERROR;
8471                    executeNextState(UnifiedRestoreState.FINAL);
8472                    return;
8473                }
8474
8475                // Pull the Package Manager metadata from the restore set first
8476                mCurrentPackage = new PackageInfo();
8477                mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
8478                mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
8479                mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
8480                if (MORE_DEBUG) {
8481                    Slog.v(TAG, "initiating restore for PMBA");
8482                }
8483                initiateOneRestore(mCurrentPackage, 0);
8484                // The PM agent called operationComplete() already, because our invocation
8485                // of it is process-local and therefore synchronous.  That means that the
8486                // next-state message (RUNNING_QUEUE) is already enqueued.  Only if we're
8487                // unable to proceed with running the queue do we remove that pending
8488                // message and jump straight to the FINAL state.  Because this was
8489                // synchronous we also know that we should cancel the pending timeout
8490                // message.
8491                mBackupHandler.removeMessages(MSG_RESTORE_OPERATION_TIMEOUT);
8492
8493                // Verify that the backup set includes metadata.  If not, we can't do
8494                // signature/version verification etc, so we simply do not proceed with
8495                // the restore operation.
8496                if (!mPmAgent.hasMetadata()) {
8497                    Slog.e(TAG, "No restore metadata available, so not restoring");
8498                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8499                            PACKAGE_MANAGER_SENTINEL,
8500                            "Package manager restore metadata missing");
8501                    mStatus = BackupTransport.TRANSPORT_ERROR;
8502                    mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
8503                    executeNextState(UnifiedRestoreState.FINAL);
8504                    return;
8505                }
8506
8507                // Success; cache the metadata and continue as expected with the
8508                // next state already enqueued
8509
8510            } catch (Exception e) {
8511                // If we lost the transport at any time, halt
8512                Slog.e(TAG, "Unable to contact transport for restore: " + e.getMessage());
8513                mStatus = BackupTransport.TRANSPORT_ERROR;
8514                mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
8515                executeNextState(UnifiedRestoreState.FINAL);
8516                return;
8517            }
8518        }
8519
8520        // state RUNNING_QUEUE : figure out what the next thing to be restored is,
8521        // and fire the appropriate next step
8522        private void dispatchNextRestore() {
8523            UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
8524            try {
8525                mRestoreDescription = mTransport.nextRestorePackage();
8526                final String pkgName = (mRestoreDescription != null)
8527                        ? mRestoreDescription.getPackageName() : null;
8528                if (pkgName == null) {
8529                    Slog.e(TAG, "Failure getting next package name");
8530                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8531                    nextState = UnifiedRestoreState.FINAL;
8532                    return;
8533                } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) {
8534                    // Yay we've reached the end cleanly
8535                    if (DEBUG) {
8536                        Slog.v(TAG, "No more packages; finishing restore");
8537                    }
8538                    int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
8539                    EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
8540                    nextState = UnifiedRestoreState.FINAL;
8541                    return;
8542                }
8543
8544                if (DEBUG) {
8545                    Slog.i(TAG, "Next restore package: " + mRestoreDescription);
8546                }
8547                sendOnRestorePackage(pkgName);
8548
8549                Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName);
8550                if (metaInfo == null) {
8551                    Slog.e(TAG, "No metadata for " + pkgName);
8552                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
8553                            "Package metadata missing");
8554                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8555                    return;
8556                }
8557
8558                try {
8559                    mCurrentPackage = mPackageManager.getPackageInfo(
8560                            pkgName, PackageManager.GET_SIGNATURES);
8561                } catch (NameNotFoundException e) {
8562                    // Whoops, we thought we could restore this package but it
8563                    // turns out not to be present.  Skip it.
8564                    Slog.e(TAG, "Package not present: " + pkgName);
8565                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
8566                            "Package missing on device");
8567                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8568                    return;
8569                }
8570
8571                if (metaInfo.versionCode > mCurrentPackage.versionCode) {
8572                    // Data is from a "newer" version of the app than we have currently
8573                    // installed.  If the app has not declared that it is prepared to
8574                    // handle this case, we do not attempt the restore.
8575                    if ((mCurrentPackage.applicationInfo.flags
8576                            & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
8577                        String message = "Version " + metaInfo.versionCode
8578                                + " > installed version " + mCurrentPackage.versionCode;
8579                        Slog.w(TAG, "Package " + pkgName + ": " + message);
8580                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8581                                pkgName, message);
8582                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
8583                        return;
8584                    } else {
8585                        if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
8586                                + " > installed " + mCurrentPackage.versionCode
8587                                + " but restoreAnyVersion");
8588                    }
8589                }
8590
8591                if (MORE_DEBUG) Slog.v(TAG, "Package " + pkgName
8592                        + " restore version [" + metaInfo.versionCode
8593                        + "] is compatible with installed version ["
8594                        + mCurrentPackage.versionCode + "]");
8595
8596                // Reset per-package preconditions and fire the appropriate next state
8597                mWidgetData = null;
8598                final int type = mRestoreDescription.getDataType();
8599                if (type == RestoreDescription.TYPE_KEY_VALUE) {
8600                    nextState = UnifiedRestoreState.RESTORE_KEYVALUE;
8601                } else if (type == RestoreDescription.TYPE_FULL_STREAM) {
8602                    nextState = UnifiedRestoreState.RESTORE_FULL;
8603                } else {
8604                    // Unknown restore type; ignore this package and move on
8605                    Slog.e(TAG, "Unrecognized restore type " + type);
8606                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8607                    return;
8608                }
8609            } catch (Exception e) {
8610                Slog.e(TAG, "Can't get next restore target from transport; halting: "
8611                        + e.getMessage());
8612                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8613                nextState = UnifiedRestoreState.FINAL;
8614                return;
8615            } finally {
8616                executeNextState(nextState);
8617            }
8618        }
8619
8620        // state RESTORE_KEYVALUE : restore one package via key/value API set
8621        private void restoreKeyValue() {
8622            // Initiating the restore will pass responsibility for the state machine's
8623            // progress to the agent callback, so we do not always execute the
8624            // next state here.
8625            final String packageName = mCurrentPackage.packageName;
8626            // Validate some semantic requirements that apply in this way
8627            // only to the key/value restore API flow
8628            if (mCurrentPackage.applicationInfo.backupAgentName == null
8629                    || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
8630                if (MORE_DEBUG) {
8631                    Slog.i(TAG, "Data exists for package " + packageName
8632                            + " but app has no agent; skipping");
8633                }
8634                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8635                        "Package has no agent");
8636                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8637                return;
8638            }
8639
8640            Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
8641            if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
8642                Slog.w(TAG, "Signature mismatch restoring " + packageName);
8643                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8644                        "Signature mismatch");
8645                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8646                return;
8647            }
8648
8649            // Good to go!  Set up and bind the agent...
8650            mAgent = bindToAgentSynchronous(
8651                    mCurrentPackage.applicationInfo,
8652                    ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
8653            if (mAgent == null) {
8654                Slog.w(TAG, "Can't find backup agent for " + packageName);
8655                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8656                        "Restore agent missing");
8657                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8658                return;
8659            }
8660
8661            // Whatever happens next, we've launched the target app now; remember that.
8662            mDidLaunch = true;
8663
8664            // And then finally start the restore on this agent
8665            try {
8666                initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
8667                ++mCount;
8668            } catch (Exception e) {
8669                Slog.e(TAG, "Error when attempting restore: " + e.toString());
8670                keyValueAgentErrorCleanup();
8671                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8672            }
8673        }
8674
8675        // Guts of a key/value restore operation
8676        void initiateOneRestore(PackageInfo app, int appVersionCode) {
8677            final String packageName = app.packageName;
8678
8679            if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
8680
8681            // !!! TODO: get the dirs from the transport
8682            mBackupDataName = new File(mDataDir, packageName + ".restore");
8683            mStageName = new File(mDataDir, packageName + ".stage");
8684            mNewStateName = new File(mStateDir, packageName + ".new");
8685            mSavedStateName = new File(mStateDir, packageName);
8686
8687            // don't stage the 'android' package where the wallpaper data lives.  this is
8688            // an optimization: we know there's no widget data hosted/published by that
8689            // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
8690            // data following the download.
8691            boolean staging = !packageName.equals("android");
8692            ParcelFileDescriptor stage;
8693            File downloadFile = (staging) ? mStageName : mBackupDataName;
8694
8695            try {
8696                // Run the transport's restore pass
8697                stage = ParcelFileDescriptor.open(downloadFile,
8698                        ParcelFileDescriptor.MODE_READ_WRITE |
8699                        ParcelFileDescriptor.MODE_CREATE |
8700                        ParcelFileDescriptor.MODE_TRUNCATE);
8701
8702                if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
8703                    // Transport-level failure, so we wind everything up and
8704                    // terminate the restore operation.
8705                    Slog.e(TAG, "Error getting restore data for " + packageName);
8706                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8707                    stage.close();
8708                    downloadFile.delete();
8709                    executeNextState(UnifiedRestoreState.FINAL);
8710                    return;
8711                }
8712
8713                // We have the data from the transport. Now we extract and strip
8714                // any per-package metadata (typically widget-related information)
8715                // if appropriate
8716                if (staging) {
8717                    stage.close();
8718                    stage = ParcelFileDescriptor.open(downloadFile,
8719                            ParcelFileDescriptor.MODE_READ_ONLY);
8720
8721                    mBackupData = ParcelFileDescriptor.open(mBackupDataName,
8722                            ParcelFileDescriptor.MODE_READ_WRITE |
8723                            ParcelFileDescriptor.MODE_CREATE |
8724                            ParcelFileDescriptor.MODE_TRUNCATE);
8725
8726                    BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
8727                    BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
8728                    byte[] buffer = new byte[8192]; // will grow when needed
8729                    while (in.readNextHeader()) {
8730                        final String key = in.getKey();
8731                        final int size = in.getDataSize();
8732
8733                        // is this a special key?
8734                        if (key.equals(KEY_WIDGET_STATE)) {
8735                            if (DEBUG) {
8736                                Slog.i(TAG, "Restoring widget state for " + packageName);
8737                            }
8738                            mWidgetData = new byte[size];
8739                            in.readEntityData(mWidgetData, 0, size);
8740                        } else {
8741                            if (size > buffer.length) {
8742                                buffer = new byte[size];
8743                            }
8744                            in.readEntityData(buffer, 0, size);
8745                            out.writeEntityHeader(key, size);
8746                            out.writeEntityData(buffer, size);
8747                        }
8748                    }
8749
8750                    mBackupData.close();
8751                }
8752
8753                // Okay, we have the data.  Now have the agent do the restore.
8754                stage.close();
8755
8756                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
8757                        ParcelFileDescriptor.MODE_READ_ONLY);
8758
8759                mNewState = ParcelFileDescriptor.open(mNewStateName,
8760                        ParcelFileDescriptor.MODE_READ_WRITE |
8761                        ParcelFileDescriptor.MODE_CREATE |
8762                        ParcelFileDescriptor.MODE_TRUNCATE);
8763
8764                // Kick off the restore, checking for hung agents.  The timeout or
8765                // the operationComplete() callback will schedule the next step,
8766                // so we do not do that here.
8767                prepareOperationTimeout(mEphemeralOpToken, TIMEOUT_RESTORE_INTERVAL,
8768                        this, OP_TYPE_RESTORE_WAIT);
8769                mAgent.doRestore(mBackupData, appVersionCode, mNewState,
8770                        mEphemeralOpToken, mBackupManagerBinder);
8771            } catch (Exception e) {
8772                Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
8773                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8774                        packageName, e.toString());
8775                keyValueAgentErrorCleanup();    // clears any pending timeout messages as well
8776
8777                // After a restore failure we go back to running the queue.  If there
8778                // are no more packages to be restored that will be handled by the
8779                // next step.
8780                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8781            }
8782        }
8783
8784        // state RESTORE_FULL : restore one package via streaming engine
8785        private void restoreFull() {
8786            // None of this can run on the work looper here, so we spin asynchronous
8787            // work like this:
8788            //
8789            //   StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk()
8790            //                       write it into the pipe to the engine
8791            //   EngineThread: FullRestoreEngine thread communicating with the target app
8792            //
8793            // When finished, StreamFeederThread executes next state as appropriate on the
8794            // backup looper, and the overall unified restore task resumes
8795            try {
8796                StreamFeederThread feeder = new StreamFeederThread();
8797                if (MORE_DEBUG) {
8798                    Slog.i(TAG, "Spinning threads for stream restore of "
8799                            + mCurrentPackage.packageName);
8800                }
8801                new Thread(feeder, "unified-stream-feeder").start();
8802
8803                // At this point the feeder is responsible for advancing the restore
8804                // state, so we're done here.
8805            } catch (IOException e) {
8806                // Unable to instantiate the feeder thread -- we need to bail on the
8807                // current target.  We haven't asked the transport for data yet, though,
8808                // so we can do that simply by going back to running the restore queue.
8809                Slog.e(TAG, "Unable to construct pipes for stream restore!");
8810                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8811            }
8812        }
8813
8814        // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
8815        private void restoreFinished() {
8816            try {
8817                prepareOperationTimeout(mEphemeralOpToken, TIMEOUT_RESTORE_FINISHED_INTERVAL, this,
8818                        OP_TYPE_RESTORE_WAIT);
8819                mAgent.doRestoreFinished(mEphemeralOpToken, mBackupManagerBinder);
8820                // If we get this far, the callback or timeout will schedule the
8821                // next restore state, so we're done
8822            } catch (Exception e) {
8823                final String packageName = mCurrentPackage.packageName;
8824                Slog.e(TAG, "Unable to finalize restore of " + packageName);
8825                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8826                        packageName, e.toString());
8827                keyValueAgentErrorCleanup();
8828                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8829            }
8830        }
8831
8832        class StreamFeederThread extends RestoreEngine implements Runnable, BackupRestoreTask {
8833            final String TAG = "StreamFeederThread";
8834            FullRestoreEngine mEngine;
8835            EngineThread mEngineThread;
8836
8837            // pipe through which we read data from the transport. [0] read, [1] write
8838            ParcelFileDescriptor[] mTransportPipes;
8839
8840            // pipe through which the engine will read data.  [0] read, [1] write
8841            ParcelFileDescriptor[] mEnginePipes;
8842
8843            private final int mEphemeralOpToken;
8844
8845            public StreamFeederThread() throws IOException {
8846                mEphemeralOpToken = generateToken();
8847                mTransportPipes = ParcelFileDescriptor.createPipe();
8848                mEnginePipes = ParcelFileDescriptor.createPipe();
8849                setRunning(true);
8850            }
8851
8852            @Override
8853            public void run() {
8854                UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
8855                int status = BackupTransport.TRANSPORT_OK;
8856
8857                EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
8858                        mCurrentPackage.packageName);
8859
8860                mEngine = new FullRestoreEngine(this, null, mCurrentPackage, false, false, mEphemeralOpToken);
8861                mEngineThread = new EngineThread(mEngine, mEnginePipes[0]);
8862
8863                ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
8864                ParcelFileDescriptor tReadEnd = mTransportPipes[0];
8865                ParcelFileDescriptor tWriteEnd = mTransportPipes[1];
8866
8867                int bufferSize = 32 * 1024;
8868                byte[] buffer = new byte[bufferSize];
8869                FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor());
8870                FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor());
8871
8872                // spin up the engine and start moving data to it
8873                new Thread(mEngineThread, "unified-restore-engine").start();
8874
8875                try {
8876                    while (status == BackupTransport.TRANSPORT_OK) {
8877                        // have the transport write some of the restoring data to us
8878                        int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd);
8879                        if (result > 0) {
8880                            // The transport wrote this many bytes of restore data to the
8881                            // pipe, so pass it along to the engine.
8882                            if (MORE_DEBUG) {
8883                                Slog.v(TAG, "  <- transport provided chunk size " + result);
8884                            }
8885                            if (result > bufferSize) {
8886                                bufferSize = result;
8887                                buffer = new byte[bufferSize];
8888                            }
8889                            int toCopy = result;
8890                            while (toCopy > 0) {
8891                                int n = transportIn.read(buffer, 0, toCopy);
8892                                engineOut.write(buffer, 0, n);
8893                                toCopy -= n;
8894                                if (MORE_DEBUG) {
8895                                    Slog.v(TAG, "  -> wrote " + n + " to engine, left=" + toCopy);
8896                                }
8897                            }
8898                        } else if (result == BackupTransport.NO_MORE_DATA) {
8899                            // Clean finish.  Wind up and we're done!
8900                            if (MORE_DEBUG) {
8901                                Slog.i(TAG, "Got clean full-restore EOF for "
8902                                        + mCurrentPackage.packageName);
8903                            }
8904                            status = BackupTransport.TRANSPORT_OK;
8905                            break;
8906                        } else {
8907                            // Transport reported some sort of failure; the fall-through
8908                            // handling will deal properly with that.
8909                            Slog.e(TAG, "Error " + result + " streaming restore for "
8910                                    + mCurrentPackage.packageName);
8911                            EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8912                            status = result;
8913                        }
8914                    }
8915                    if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through");
8916                } catch (IOException e) {
8917                    // We lost our ability to communicate via the pipes.  That's worrying
8918                    // but potentially recoverable; abandon this package's restore but
8919                    // carry on with the next restore target.
8920                    Slog.e(TAG, "Unable to route data for restore");
8921                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8922                            mCurrentPackage.packageName, "I/O error on pipes");
8923                    status = BackupTransport.AGENT_ERROR;
8924                } catch (Exception e) {
8925                    // The transport threw; terminate the whole operation.  Closing
8926                    // the sockets will wake up the engine and it will then tidy up the
8927                    // remote end.
8928                    Slog.e(TAG, "Transport failed during restore: " + e.getMessage());
8929                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8930                    status = BackupTransport.TRANSPORT_ERROR;
8931                } finally {
8932                    // Close the transport pipes and *our* end of the engine pipe,
8933                    // but leave the engine thread's end open so that it properly
8934                    // hits EOF and winds up its operations.
8935                    IoUtils.closeQuietly(mEnginePipes[1]);
8936                    IoUtils.closeQuietly(mTransportPipes[0]);
8937                    IoUtils.closeQuietly(mTransportPipes[1]);
8938
8939                    // Don't proceed until the engine has wound up operations
8940                    mEngineThread.waitForResult();
8941
8942                    // Now we're really done with this one too
8943                    IoUtils.closeQuietly(mEnginePipes[0]);
8944
8945                    // In all cases we want to remember whether we launched
8946                    // the target app as part of our work so far.
8947                    mDidLaunch = (mEngine.getAgent() != null);
8948
8949                    // If we hit a transport-level error, we are done with everything;
8950                    // if we hit an agent error we just go back to running the queue.
8951                    if (status == BackupTransport.TRANSPORT_OK) {
8952                        // Clean finish means we issue the restore-finished callback
8953                        nextState = UnifiedRestoreState.RESTORE_FINISHED;
8954
8955                        // the engine bound the target's agent, so recover that binding
8956                        // to use for the callback.
8957                        mAgent = mEngine.getAgent();
8958
8959                        // and the restored widget data, if any
8960                        mWidgetData = mEngine.getWidgetData();
8961                    } else {
8962                        // Something went wrong somewhere.  Whether it was at the transport
8963                        // level is immaterial; we need to tell the transport to bail
8964                        try {
8965                            mTransport.abortFullRestore();
8966                        } catch (Exception e) {
8967                            // transport itself is dead; make sure we handle this as a
8968                            // fatal error
8969                            Slog.e(TAG, "Transport threw from abortFullRestore: " + e.getMessage());
8970                            status = BackupTransport.TRANSPORT_ERROR;
8971                        }
8972
8973                        // We also need to wipe the current target's data, as it's probably
8974                        // in an incoherent state.
8975                        clearApplicationDataSynchronous(mCurrentPackage.packageName);
8976
8977                        // Schedule the next state based on the nature of our failure
8978                        if (status == BackupTransport.TRANSPORT_ERROR) {
8979                            nextState = UnifiedRestoreState.FINAL;
8980                        } else {
8981                            nextState = UnifiedRestoreState.RUNNING_QUEUE;
8982                        }
8983                    }
8984                    executeNextState(nextState);
8985                    setRunning(false);
8986                }
8987            }
8988
8989            // BackupRestoreTask interface, specifically for timeout handling
8990
8991            @Override
8992            public void execute() { /* intentionally empty */ }
8993
8994            @Override
8995            public void operationComplete(long result) { /* intentionally empty */ }
8996
8997            // The app has timed out handling a restoring file
8998            @Override
8999            public void handleCancel(boolean cancelAll) {
9000                removeOperation(mEphemeralOpToken);
9001                if (DEBUG) {
9002                    Slog.w(TAG, "Full-data restore target timed out; shutting down");
9003                }
9004                mMonitor = monitorEvent(mMonitor, BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT,
9005                        mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
9006                mEngineThread.handleTimeout();
9007
9008                IoUtils.closeQuietly(mEnginePipes[1]);
9009                mEnginePipes[1] = null;
9010                IoUtils.closeQuietly(mEnginePipes[0]);
9011                mEnginePipes[0] = null;
9012            }
9013        }
9014
9015        class EngineThread implements Runnable {
9016            FullRestoreEngine mEngine;
9017            FileInputStream mEngineStream;
9018
9019            EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
9020                mEngine = engine;
9021                engine.setRunning(true);
9022                // We *do* want this FileInputStream to own the underlying fd, so that
9023                // when we are finished with it, it closes this end of the pipe in a way
9024                // that signals its other end.
9025                mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true);
9026            }
9027
9028            public boolean isRunning() {
9029                return mEngine.isRunning();
9030            }
9031
9032            public int waitForResult() {
9033                return mEngine.waitForResult();
9034            }
9035
9036            @Override
9037            public void run() {
9038                try {
9039                    while (mEngine.isRunning()) {
9040                        // Tell it to be sure to leave the agent instance up after finishing
9041                        mEngine.restoreOneFile(mEngineStream, false);
9042                    }
9043                } finally {
9044                    // Because mEngineStream adopted its underlying FD, this also
9045                    // closes this end of the pipe.
9046                    IoUtils.closeQuietly(mEngineStream);
9047                }
9048            }
9049
9050            public void handleTimeout() {
9051                IoUtils.closeQuietly(mEngineStream);
9052                mEngine.handleTimeout();
9053            }
9054        }
9055
9056        // state FINAL : tear everything down and we're done.
9057        private void finalizeRestore() {
9058            if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
9059
9060            try {
9061                mTransport.finishRestore();
9062            } catch (Exception e) {
9063                Slog.e(TAG, "Error finishing restore", e);
9064            }
9065
9066            // Tell the observer we're done
9067            if (mObserver != null) {
9068                try {
9069                    mObserver.restoreFinished(mStatus);
9070                } catch (RemoteException e) {
9071                    Slog.d(TAG, "Restore observer died at restoreFinished");
9072                }
9073            }
9074
9075            // Clear any ongoing session timeout.
9076            mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
9077
9078            // If we have a PM token, we must under all circumstances be sure to
9079            // handshake when we've finished.
9080            if (mPmToken > 0) {
9081                if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
9082                try {
9083                    mPackageManagerBinder.finishPackageInstall(mPmToken, mDidLaunch);
9084                } catch (RemoteException e) { /* can't happen */ }
9085            } else {
9086                // We were invoked via an active restore session, not by the Package
9087                // Manager, so start up the session timeout again.
9088                mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
9089                        TIMEOUT_RESTORE_INTERVAL);
9090            }
9091
9092            // Kick off any work that may be needed regarding app widget restores
9093            // TODO: http://b/22388012
9094            AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM);
9095
9096            // If this was a full-system restore, record the ancestral
9097            // dataset information
9098            if (mIsSystemRestore && mPmAgent != null) {
9099                mAncestralPackages = mPmAgent.getRestoredPackages();
9100                mAncestralToken = mToken;
9101                writeRestoreTokens();
9102            }
9103
9104            // done; we can finally release the wakelock and be legitimately done.
9105            Slog.i(TAG, "Restore complete.");
9106            mWakelock.release();
9107        }
9108
9109        void keyValueAgentErrorCleanup() {
9110            // If the agent fails restore, it might have put the app's data
9111            // into an incoherent state.  For consistency we wipe its data
9112            // again in this case before continuing with normal teardown
9113            clearApplicationDataSynchronous(mCurrentPackage.packageName);
9114            keyValueAgentCleanup();
9115        }
9116
9117        // TODO: clean up naming; this is now used at finish by both k/v and stream restores
9118        void keyValueAgentCleanup() {
9119            mBackupDataName.delete();
9120            mStageName.delete();
9121            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
9122            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
9123            mBackupData = mNewState = null;
9124
9125            // if everything went okay, remember the recorded state now
9126            //
9127            // !!! TODO: the restored data could be migrated on the server
9128            // side into the current dataset.  In that case the new state file
9129            // we just created would reflect the data already extant in the
9130            // backend, so there'd be nothing more to do.  Until that happens,
9131            // however, we need to make sure that we record the data to the
9132            // current backend dataset.  (Yes, this means shipping the data over
9133            // the wire in both directions.  That's bad, but consistency comes
9134            // first, then efficiency.)  Once we introduce server-side data
9135            // migration to the newly-restored device's dataset, we will change
9136            // the following from a discard of the newly-written state to the
9137            // "correct" operation of renaming into the canonical state blob.
9138            mNewStateName.delete();                      // TODO: remove; see above comment
9139            //mNewStateName.renameTo(mSavedStateName);   // TODO: replace with this
9140
9141            // If this wasn't the PM pseudopackage, tear down the agent side
9142            if (mCurrentPackage.applicationInfo != null) {
9143                // unbind and tidy up even on timeout or failure
9144                try {
9145                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
9146
9147                    // The agent was probably running with a stub Application object,
9148                    // which isn't a valid run mode for the main app logic.  Shut
9149                    // down the app so that next time it's launched, it gets the
9150                    // usual full initialization.  Note that this is only done for
9151                    // full-system restores: when a single app has requested a restore,
9152                    // it is explicitly not killed following that operation.
9153                    //
9154                    // We execute this kill when these conditions hold:
9155                    //    1. it's not a system-uid process,
9156                    //    2. the app did not request its own restore (mTargetPackage == null), and either
9157                    //    3a. the app is a full-data target (TYPE_FULL_STREAM) or
9158                    //     b. the app does not state android:killAfterRestore="false" in its manifest
9159                    final int appFlags = mCurrentPackage.applicationInfo.flags;
9160                    final boolean killAfterRestore =
9161                            (mCurrentPackage.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
9162                            && ((mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM)
9163                                    || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0));
9164
9165                    if (mTargetPackage == null && killAfterRestore) {
9166                        if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
9167                                + mCurrentPackage.applicationInfo.processName);
9168                        mActivityManager.killApplicationProcess(
9169                                mCurrentPackage.applicationInfo.processName,
9170                                mCurrentPackage.applicationInfo.uid);
9171                    }
9172                } catch (RemoteException e) {
9173                    // can't happen; we run in the same process as the activity manager
9174                }
9175            }
9176
9177            // The caller is responsible for reestablishing the state machine; our
9178            // responsibility here is to clear the decks for whatever comes next.
9179            mBackupHandler.removeMessages(MSG_RESTORE_OPERATION_TIMEOUT, this);
9180        }
9181
9182        @Override
9183        public void operationComplete(long unusedResult) {
9184            removeOperation(mEphemeralOpToken);
9185            if (MORE_DEBUG) {
9186                Slog.i(TAG, "operationComplete() during restore: target="
9187                        + mCurrentPackage.packageName
9188                        + " state=" + mState);
9189            }
9190
9191            final UnifiedRestoreState nextState;
9192            switch (mState) {
9193                case INITIAL:
9194                    // We've just (manually) restored the PMBA.  It doesn't need the
9195                    // additional restore-finished callback so we bypass that and go
9196                    // directly to running the queue.
9197                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
9198                    break;
9199
9200                case RESTORE_KEYVALUE:
9201                case RESTORE_FULL: {
9202                    // Okay, we've just heard back from the agent that it's done with
9203                    // the restore itself.  We now have to send the same agent its
9204                    // doRestoreFinished() callback, so roll into that state.
9205                    nextState = UnifiedRestoreState.RESTORE_FINISHED;
9206                    break;
9207                }
9208
9209                case RESTORE_FINISHED: {
9210                    // Okay, we're done with this package.  Tidy up and go on to the next
9211                    // app in the queue.
9212                    int size = (int) mBackupDataName.length();
9213                    EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
9214                            mCurrentPackage.packageName, size);
9215
9216                    // Just go back to running the restore queue
9217                    keyValueAgentCleanup();
9218
9219                    // If there was widget state associated with this app, get the OS to
9220                    // incorporate it into current bookeeping and then pass that along to
9221                    // the app as part of the restore-time work.
9222                    if (mWidgetData != null) {
9223                        restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
9224                    }
9225
9226                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
9227                    break;
9228                }
9229
9230                default: {
9231                    // Some kind of horrible semantic error; we're in an unexpected state.
9232                    // Back off hard and wind up.
9233                    Slog.e(TAG, "Unexpected restore callback into state " + mState);
9234                    keyValueAgentErrorCleanup();
9235                    nextState = UnifiedRestoreState.FINAL;
9236                    break;
9237                }
9238            }
9239
9240            executeNextState(nextState);
9241        }
9242
9243        // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
9244        @Override
9245        public void handleCancel(boolean cancelAll) {
9246            removeOperation(mEphemeralOpToken);
9247            Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
9248            mMonitor = monitorEvent(mMonitor, BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT,
9249                    mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT);
9250            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
9251                    mCurrentPackage.packageName, "restore timeout");
9252            // Handle like an agent that threw on invocation: wipe it and go on to the next
9253            keyValueAgentErrorCleanup();
9254            executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
9255        }
9256
9257        void executeNextState(UnifiedRestoreState nextState) {
9258            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
9259                    + this + " nextState=" + nextState);
9260            mState = nextState;
9261            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
9262            mBackupHandler.sendMessage(msg);
9263        }
9264
9265        // restore observer support
9266        void sendStartRestore(int numPackages) {
9267            if (mObserver != null) {
9268                try {
9269                    mObserver.restoreStarting(numPackages);
9270                } catch (RemoteException e) {
9271                    Slog.w(TAG, "Restore observer went away: startRestore");
9272                    mObserver = null;
9273                }
9274            }
9275        }
9276
9277        void sendOnRestorePackage(String name) {
9278            if (mObserver != null) {
9279                if (mObserver != null) {
9280                    try {
9281                        mObserver.onUpdate(mCount, name);
9282                    } catch (RemoteException e) {
9283                        Slog.d(TAG, "Restore observer died in onUpdate");
9284                        mObserver = null;
9285                    }
9286                }
9287            }
9288        }
9289
9290        void sendEndRestore() {
9291            if (mObserver != null) {
9292                try {
9293                    mObserver.restoreFinished(mStatus);
9294                } catch (RemoteException e) {
9295                    Slog.w(TAG, "Restore observer went away: endRestore");
9296                    mObserver = null;
9297                }
9298            }
9299        }
9300    }
9301
9302    class PerformClearTask implements Runnable {
9303        IBackupTransport mTransport;
9304        PackageInfo mPackage;
9305
9306        PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
9307            mTransport = transport;
9308            mPackage = packageInfo;
9309        }
9310
9311        public void run() {
9312            try {
9313                // Clear the on-device backup state to ensure a full backup next time
9314                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
9315                File stateFile = new File(stateDir, mPackage.packageName);
9316                stateFile.delete();
9317
9318                // Tell the transport to remove all the persistent storage for the app
9319                // TODO - need to handle failures
9320                mTransport.clearBackupData(mPackage);
9321            } catch (Exception e) {
9322                Slog.e(TAG, "Transport threw clearing data for " + mPackage + ": " + e.getMessage());
9323            } finally {
9324                try {
9325                    // TODO - need to handle failures
9326                    mTransport.finishBackup();
9327                } catch (Exception e) {
9328                    // Nothing we can do here, alas
9329                    Slog.e(TAG, "Unable to mark clear operation finished: " + e.getMessage());
9330                }
9331
9332                // Last but not least, release the cpu
9333                mWakelock.release();
9334            }
9335        }
9336    }
9337
9338    class PerformInitializeTask implements Runnable {
9339        HashSet<String> mQueue;
9340
9341        PerformInitializeTask(HashSet<String> transportNames) {
9342            mQueue = transportNames;
9343        }
9344
9345        public void run() {
9346            try {
9347                for (String transportName : mQueue) {
9348                    IBackupTransport transport =
9349                            mTransportManager.getTransportBinder(transportName);
9350                    if (transport == null) {
9351                        Slog.e(TAG, "Requested init for " + transportName + " but not found");
9352                        continue;
9353                    }
9354
9355                    Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
9356                    EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
9357                    long startRealtime = SystemClock.elapsedRealtime();
9358                    int status = transport.initializeDevice();
9359
9360                    if (status == BackupTransport.TRANSPORT_OK) {
9361                        status = transport.finishBackup();
9362                    }
9363
9364                    // Okay, the wipe really happened.  Clean up our local bookkeeping.
9365                    if (status == BackupTransport.TRANSPORT_OK) {
9366                        Slog.i(TAG, "Device init successful");
9367                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
9368                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
9369                        resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
9370                        EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
9371                        synchronized (mQueueLock) {
9372                            recordInitPendingLocked(false, transportName);
9373                        }
9374                    } else {
9375                        // If this didn't work, requeue this one and try again
9376                        // after a suitable interval
9377                        Slog.e(TAG, "Transport error in initializeDevice()");
9378                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
9379                        synchronized (mQueueLock) {
9380                            recordInitPendingLocked(true, transportName);
9381                        }
9382                        // do this via another alarm to make sure of the wakelock states
9383                        long delay = transport.requestBackupTime();
9384                        Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay);
9385                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
9386                                System.currentTimeMillis() + delay, mRunInitIntent);
9387                    }
9388                }
9389            } catch (Exception e) {
9390                Slog.e(TAG, "Unexpected error performing init", e);
9391            } finally {
9392                // Done; release the wakelock
9393                mWakelock.release();
9394            }
9395        }
9396    }
9397
9398    private void dataChangedImpl(String packageName) {
9399        HashSet<String> targets = dataChangedTargets(packageName);
9400        dataChangedImpl(packageName, targets);
9401    }
9402
9403    private void dataChangedImpl(String packageName, HashSet<String> targets) {
9404        // Record that we need a backup pass for the caller.  Since multiple callers
9405        // may share a uid, we need to note all candidates within that uid and schedule
9406        // a backup pass for each of them.
9407        if (targets == null) {
9408            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
9409                   + " uid=" + Binder.getCallingUid());
9410            return;
9411        }
9412
9413        synchronized (mQueueLock) {
9414            // Note that this client has made data changes that need to be backed up
9415            if (targets.contains(packageName)) {
9416                // Add the caller to the set of pending backups.  If there is
9417                // one already there, then overwrite it, but no harm done.
9418                BackupRequest req = new BackupRequest(packageName);
9419                if (mPendingBackups.put(packageName, req) == null) {
9420                    if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
9421
9422                    // Journal this request in case of crash.  The put()
9423                    // operation returned null when this package was not already
9424                    // in the set; we want to avoid touching the disk redundantly.
9425                    writeToJournalLocked(packageName);
9426                }
9427            }
9428        }
9429
9430        // ...and schedule a backup pass if necessary
9431        KeyValueBackupJob.schedule(mContext);
9432    }
9433
9434    // Note: packageName is currently unused, but may be in the future
9435    private HashSet<String> dataChangedTargets(String packageName) {
9436        // If the caller does not hold the BACKUP permission, it can only request a
9437        // backup of its own data.
9438        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
9439                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
9440            synchronized (mBackupParticipants) {
9441                return mBackupParticipants.get(Binder.getCallingUid());
9442            }
9443        }
9444
9445        // a caller with full permission can ask to back up any participating app
9446        HashSet<String> targets = new HashSet<String>();
9447        if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
9448            targets.add(PACKAGE_MANAGER_SENTINEL);
9449        } else {
9450            synchronized (mBackupParticipants) {
9451                int N = mBackupParticipants.size();
9452                for (int i = 0; i < N; i++) {
9453                    HashSet<String> s = mBackupParticipants.valueAt(i);
9454                    if (s != null) {
9455                        targets.addAll(s);
9456                    }
9457                }
9458            }
9459        }
9460        return targets;
9461    }
9462
9463    private void writeToJournalLocked(String str) {
9464        RandomAccessFile out = null;
9465        try {
9466            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
9467            out = new RandomAccessFile(mJournal, "rws");
9468            out.seek(out.length());
9469            out.writeUTF(str);
9470        } catch (IOException e) {
9471            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
9472            mJournal = null;
9473        } finally {
9474            try { if (out != null) out.close(); } catch (IOException e) {}
9475        }
9476    }
9477
9478    // ----- IBackupManager binder interface -----
9479
9480    public void dataChanged(final String packageName) {
9481        final int callingUserHandle = UserHandle.getCallingUserId();
9482        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9483            // TODO: http://b/22388012
9484            // App is running under a non-owner user profile.  For now, we do not back
9485            // up data from secondary user profiles.
9486            // TODO: backups for all user profiles although don't add backup for profiles
9487            // without adding admin control in DevicePolicyManager.
9488            if (MORE_DEBUG) {
9489                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
9490                        + callingUserHandle);
9491            }
9492            return;
9493        }
9494
9495        final HashSet<String> targets = dataChangedTargets(packageName);
9496        if (targets == null) {
9497            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
9498                   + " uid=" + Binder.getCallingUid());
9499            return;
9500        }
9501
9502        mBackupHandler.post(new Runnable() {
9503                public void run() {
9504                    dataChangedImpl(packageName, targets);
9505                }
9506            });
9507    }
9508
9509    // Clear the given package's backup data from the current transport
9510    public void clearBackupData(String transportName, String packageName) {
9511        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
9512        PackageInfo info;
9513        try {
9514            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
9515        } catch (NameNotFoundException e) {
9516            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
9517            return;
9518        }
9519
9520        // If the caller does not hold the BACKUP permission, it can only request a
9521        // wipe of its own backed-up data.
9522        HashSet<String> apps;
9523        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
9524                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
9525            apps = mBackupParticipants.get(Binder.getCallingUid());
9526        } else {
9527            // a caller with full permission can ask to back up any participating app
9528            // !!! TODO: allow data-clear of ANY app?
9529            if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
9530            apps = new HashSet<String>();
9531            int N = mBackupParticipants.size();
9532            for (int i = 0; i < N; i++) {
9533                HashSet<String> s = mBackupParticipants.valueAt(i);
9534                if (s != null) {
9535                    apps.addAll(s);
9536                }
9537            }
9538        }
9539
9540        // Is the given app an available participant?
9541        if (apps.contains(packageName)) {
9542            // found it; fire off the clear request
9543            if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
9544            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
9545            synchronized (mQueueLock) {
9546                final IBackupTransport transport =
9547                        mTransportManager.getTransportBinder(transportName);
9548                if (transport == null) {
9549                    // transport is currently unavailable -- make sure to retry
9550                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
9551                            new ClearRetryParams(transportName, packageName));
9552                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
9553                    return;
9554                }
9555                long oldId = Binder.clearCallingIdentity();
9556                mWakelock.acquire();
9557                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
9558                        new ClearParams(transport, info));
9559                mBackupHandler.sendMessage(msg);
9560                Binder.restoreCallingIdentity(oldId);
9561            }
9562        }
9563    }
9564
9565    // Run a backup pass immediately for any applications that have declared
9566    // that they have pending updates.
9567    public void backupNow() {
9568        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
9569
9570        if (mPowerManager.isPowerSaveMode()) {
9571            if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
9572            KeyValueBackupJob.schedule(mContext);   // try again in several hours
9573        } else {
9574            if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
9575            synchronized (mQueueLock) {
9576                // Fire the intent that kicks off the whole shebang...
9577                try {
9578                    mRunBackupIntent.send();
9579                } catch (PendingIntent.CanceledException e) {
9580                    // should never happen
9581                    Slog.e(TAG, "run-backup intent cancelled!");
9582                }
9583
9584                // ...and cancel any pending scheduled job, because we've just superseded it
9585                KeyValueBackupJob.cancel(mContext);
9586            }
9587        }
9588    }
9589
9590    boolean deviceIsProvisioned() {
9591        final ContentResolver resolver = mContext.getContentResolver();
9592        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
9593    }
9594
9595    // Run a *full* backup pass for the given packages, writing the resulting data stream
9596    // to the supplied file descriptor.  This method is synchronous and does not return
9597    // to the caller until the backup has been completed.
9598    //
9599    // This is the variant used by 'adb backup'; it requires on-screen confirmation
9600    // by the user because it can be used to offload data over untrusted USB.
9601    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
9602            boolean includeObbs, boolean includeShared, boolean doWidgets,
9603            boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) {
9604        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
9605
9606        final int callingUserHandle = UserHandle.getCallingUserId();
9607        // TODO: http://b/22388012
9608        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9609            throw new IllegalStateException("Backup supported only for the device owner");
9610        }
9611
9612        // Validate
9613        if (!doAllApps) {
9614            if (!includeShared) {
9615                // If we're backing up shared data (sdcard or equivalent), then we can run
9616                // without any supplied app names.  Otherwise, we'd be doing no work, so
9617                // report the error.
9618                if (pkgList == null || pkgList.length == 0) {
9619                    throw new IllegalArgumentException(
9620                            "Backup requested but neither shared nor any apps named");
9621                }
9622            }
9623        }
9624
9625        long oldId = Binder.clearCallingIdentity();
9626        try {
9627            // Doesn't make sense to do a full backup prior to setup
9628            if (!deviceIsProvisioned()) {
9629                Slog.i(TAG, "Full backup not supported before setup");
9630                return;
9631            }
9632
9633            if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
9634                    + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
9635                    + " system=" + includeSystem + " pkgs=" + pkgList);
9636            Slog.i(TAG, "Beginning full backup...");
9637
9638            FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
9639                    includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList);
9640            final int token = generateToken();
9641            synchronized (mFullConfirmations) {
9642                mFullConfirmations.put(token, params);
9643            }
9644
9645            // start up the confirmation UI
9646            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
9647            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
9648                Slog.e(TAG, "Unable to launch full backup confirmation");
9649                mFullConfirmations.delete(token);
9650                return;
9651            }
9652
9653            // make sure the screen is lit for the user interaction
9654            mPowerManager.userActivity(SystemClock.uptimeMillis(),
9655                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
9656                    0);
9657
9658            // start the confirmation countdown
9659            startConfirmationTimeout(token, params);
9660
9661            // wait for the backup to be performed
9662            if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
9663            waitForCompletion(params);
9664        } finally {
9665            try {
9666                fd.close();
9667            } catch (IOException e) {
9668                // just eat it
9669            }
9670            Binder.restoreCallingIdentity(oldId);
9671            Slog.d(TAG, "Full backup processing complete.");
9672        }
9673    }
9674
9675    public void fullTransportBackup(String[] pkgNames) {
9676        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
9677                "fullTransportBackup");
9678
9679        final int callingUserHandle = UserHandle.getCallingUserId();
9680        // TODO: http://b/22388012
9681        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9682            throw new IllegalStateException("Restore supported only for the device owner");
9683        }
9684
9685        if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
9686            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
9687        } else {
9688            if (DEBUG) {
9689                Slog.d(TAG, "fullTransportBackup()");
9690            }
9691
9692            final long oldId = Binder.clearCallingIdentity();
9693            try {
9694                CountDownLatch latch = new CountDownLatch(1);
9695                PerformFullTransportBackupTask task = new PerformFullTransportBackupTask(null,
9696                        pkgNames, false, null, latch, null, null, false /* userInitiated */);
9697                // Acquiring wakelock for PerformFullTransportBackupTask before its start.
9698                mWakelock.acquire();
9699                (new Thread(task, "full-transport-master")).start();
9700                do {
9701                    try {
9702                        latch.await();
9703                        break;
9704                    } catch (InterruptedException e) {
9705                        // Just go back to waiting for the latch to indicate completion
9706                    }
9707                } while (true);
9708
9709                // We just ran a backup on these packages, so kick them to the end of the queue
9710                final long now = System.currentTimeMillis();
9711                for (String pkg : pkgNames) {
9712                    enqueueFullBackup(pkg, now);
9713                }
9714            } finally {
9715                Binder.restoreCallingIdentity(oldId);
9716            }
9717        }
9718
9719        if (DEBUG) {
9720            Slog.d(TAG, "Done with full transport backup.");
9721        }
9722    }
9723
9724    public void fullRestore(ParcelFileDescriptor fd) {
9725        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
9726
9727        final int callingUserHandle = UserHandle.getCallingUserId();
9728        // TODO: http://b/22388012
9729        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9730            throw new IllegalStateException("Restore supported only for the device owner");
9731        }
9732
9733        long oldId = Binder.clearCallingIdentity();
9734
9735        try {
9736            // Check whether the device has been provisioned -- we don't handle
9737            // full restores prior to completing the setup process.
9738            if (!deviceIsProvisioned()) {
9739                Slog.i(TAG, "Full restore not permitted before setup");
9740                return;
9741            }
9742
9743            Slog.i(TAG, "Beginning full restore...");
9744
9745            FullRestoreParams params = new FullRestoreParams(fd);
9746            final int token = generateToken();
9747            synchronized (mFullConfirmations) {
9748                mFullConfirmations.put(token, params);
9749            }
9750
9751            // start up the confirmation UI
9752            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
9753            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
9754                Slog.e(TAG, "Unable to launch full restore confirmation");
9755                mFullConfirmations.delete(token);
9756                return;
9757            }
9758
9759            // make sure the screen is lit for the user interaction
9760            mPowerManager.userActivity(SystemClock.uptimeMillis(),
9761                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
9762                    0);
9763
9764            // start the confirmation countdown
9765            startConfirmationTimeout(token, params);
9766
9767            // wait for the restore to be performed
9768            if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
9769            waitForCompletion(params);
9770        } finally {
9771            try {
9772                fd.close();
9773            } catch (IOException e) {
9774                Slog.w(TAG, "Error trying to close fd after full restore: " + e);
9775            }
9776            Binder.restoreCallingIdentity(oldId);
9777            Slog.i(TAG, "Full restore processing complete.");
9778        }
9779    }
9780
9781    boolean startConfirmationUi(int token, String action) {
9782        try {
9783            Intent confIntent = new Intent(action);
9784            confIntent.setClassName("com.android.backupconfirm",
9785                    "com.android.backupconfirm.BackupRestoreConfirmation");
9786            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
9787            confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
9788            mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
9789        } catch (ActivityNotFoundException e) {
9790            return false;
9791        }
9792        return true;
9793    }
9794
9795    void startConfirmationTimeout(int token, FullParams params) {
9796        if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
9797                + TIMEOUT_FULL_CONFIRMATION + " millis");
9798        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
9799                token, 0, params);
9800        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
9801    }
9802
9803    void waitForCompletion(FullParams params) {
9804        synchronized (params.latch) {
9805            while (params.latch.get() == false) {
9806                try {
9807                    params.latch.wait();
9808                } catch (InterruptedException e) { /* never interrupted */ }
9809            }
9810        }
9811    }
9812
9813    void signalFullBackupRestoreCompletion(FullParams params) {
9814        synchronized (params.latch) {
9815            params.latch.set(true);
9816            params.latch.notifyAll();
9817        }
9818    }
9819
9820    // Confirm that the previously-requested full backup/restore operation can proceed.  This
9821    // is used to require a user-facing disclosure about the operation.
9822    public void acknowledgeFullBackupOrRestore(int token, boolean allow,
9823            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
9824        if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
9825                + " allow=" + allow);
9826
9827        // TODO: possibly require not just this signature-only permission, but even
9828        // require that the specific designated confirmation-UI app uid is the caller?
9829        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
9830
9831        long oldId = Binder.clearCallingIdentity();
9832        try {
9833
9834            FullParams params;
9835            synchronized (mFullConfirmations) {
9836                params = mFullConfirmations.get(token);
9837                if (params != null) {
9838                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
9839                    mFullConfirmations.delete(token);
9840
9841                    if (allow) {
9842                        final int verb = params instanceof FullBackupParams
9843                                ? MSG_RUN_ADB_BACKUP
9844                                : MSG_RUN_ADB_RESTORE;
9845
9846                        params.observer = observer;
9847                        params.curPassword = curPassword;
9848
9849                        params.encryptPassword = encPpassword;
9850
9851                        if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
9852                        mWakelock.acquire();
9853                        Message msg = mBackupHandler.obtainMessage(verb, params);
9854                        mBackupHandler.sendMessage(msg);
9855                    } else {
9856                        Slog.w(TAG, "User rejected full backup/restore operation");
9857                        // indicate completion without having actually transferred any data
9858                        signalFullBackupRestoreCompletion(params);
9859                    }
9860                } else {
9861                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
9862                }
9863            }
9864        } finally {
9865            Binder.restoreCallingIdentity(oldId);
9866        }
9867    }
9868
9869    private static boolean backupSettingMigrated(int userId) {
9870        File base = new File(Environment.getDataDirectory(), "backup");
9871        File enableFile = new File(base, BACKUP_ENABLE_FILE);
9872        return enableFile.exists();
9873    }
9874
9875    private static boolean readBackupEnableState(int userId) {
9876        File base = new File(Environment.getDataDirectory(), "backup");
9877        File enableFile = new File(base, BACKUP_ENABLE_FILE);
9878        if (enableFile.exists()) {
9879            try (FileInputStream fin = new FileInputStream(enableFile)) {
9880                int state = fin.read();
9881                return state != 0;
9882            } catch (IOException e) {
9883                // can't read the file; fall through to assume disabled
9884                Slog.e(TAG, "Cannot read enable state; assuming disabled");
9885            }
9886        } else {
9887            if (DEBUG) {
9888                Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
9889            }
9890        }
9891        return false;
9892    }
9893
9894    private static void writeBackupEnableState(boolean enable, int userId) {
9895        File base = new File(Environment.getDataDirectory(), "backup");
9896        File enableFile = new File(base, BACKUP_ENABLE_FILE);
9897        File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
9898        FileOutputStream fout = null;
9899        try {
9900            fout = new FileOutputStream(stage);
9901            fout.write(enable ? 1 : 0);
9902            fout.close();
9903            stage.renameTo(enableFile);
9904            // will be synced immediately by the try-with-resources call to close()
9905        } catch (IOException|RuntimeException e) {
9906            // Whoops; looks like we're doomed.  Roll everything out, disabled,
9907            // including the legacy state.
9908            Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
9909                    + e.getMessage());
9910
9911            final ContentResolver r = sInstance.mContext.getContentResolver();
9912            Settings.Secure.putStringForUser(r,
9913                    Settings.Secure.BACKUP_ENABLED, null, userId);
9914            enableFile.delete();
9915            stage.delete();
9916        } finally {
9917            IoUtils.closeQuietly(fout);
9918        }
9919    }
9920
9921    // Enable/disable backups
9922    public void setBackupEnabled(boolean enable) {
9923        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9924                "setBackupEnabled");
9925
9926        Slog.i(TAG, "Backup enabled => " + enable);
9927
9928        long oldId = Binder.clearCallingIdentity();
9929        try {
9930            boolean wasEnabled = mEnabled;
9931            synchronized (this) {
9932                writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
9933                mEnabled = enable;
9934            }
9935
9936            synchronized (mQueueLock) {
9937                if (enable && !wasEnabled && mProvisioned) {
9938                    // if we've just been enabled, start scheduling backup passes
9939                    KeyValueBackupJob.schedule(mContext);
9940                    scheduleNextFullBackupJob(0);
9941                } else if (!enable) {
9942                    // No longer enabled, so stop running backups
9943                    if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
9944
9945                    KeyValueBackupJob.cancel(mContext);
9946
9947                    // This also constitutes an opt-out, so we wipe any data for
9948                    // this device from the backend.  We start that process with
9949                    // an alarm in order to guarantee wakelock states.
9950                    if (wasEnabled && mProvisioned) {
9951                        // NOTE: we currently flush every registered transport, not just
9952                        // the currently-active one.
9953                        String[] allTransports = mTransportManager.getBoundTransportNames();
9954                        // build the set of transports for which we are posting an init
9955                        for (String transport : allTransports) {
9956                            recordInitPendingLocked(true, transport);
9957                        }
9958                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
9959                                mRunInitIntent);
9960                    }
9961                }
9962            }
9963        } finally {
9964            Binder.restoreCallingIdentity(oldId);
9965        }
9966    }
9967
9968    // Enable/disable automatic restore of app data at install time
9969    public void setAutoRestore(boolean doAutoRestore) {
9970        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9971                "setAutoRestore");
9972
9973        Slog.i(TAG, "Auto restore => " + doAutoRestore);
9974
9975        final long oldId = Binder.clearCallingIdentity();
9976        try {
9977            synchronized (this) {
9978                Settings.Secure.putInt(mContext.getContentResolver(),
9979                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
9980                mAutoRestore = doAutoRestore;
9981            }
9982        } finally {
9983            Binder.restoreCallingIdentity(oldId);
9984        }
9985    }
9986
9987    // Mark the backup service as having been provisioned
9988    public void setBackupProvisioned(boolean available) {
9989        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9990                "setBackupProvisioned");
9991        /*
9992         * This is now a no-op; provisioning is simply the device's own setup state.
9993         */
9994    }
9995
9996    // Report whether the backup mechanism is currently enabled
9997    public boolean isBackupEnabled() {
9998        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
9999        return mEnabled;    // no need to synchronize just to read it
10000    }
10001
10002    // Report the name of the currently active transport
10003    public String getCurrentTransport() {
10004        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10005                "getCurrentTransport");
10006        String currentTransport = mTransportManager.getCurrentTransportName();
10007        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
10008        return currentTransport;
10009    }
10010
10011    // Report all known, available backup transports
10012    public String[] listAllTransports() {
10013        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
10014
10015        return mTransportManager.getBoundTransportNames();
10016    }
10017
10018    public ComponentName[] listAllTransportComponents() {
10019        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10020                "listAllTransportComponents");
10021        return mTransportManager.getAllTransportCompenents();
10022    }
10023
10024    public String[] getTransportWhitelist() {
10025        // No permission check, intentionally.
10026        Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
10027        String[] whitelistedTransports = new String[whitelistedComponents.size()];
10028        int i = 0;
10029        for (ComponentName component : whitelistedComponents) {
10030            whitelistedTransports[i] = component.flattenToShortString();
10031            i++;
10032        }
10033        return whitelistedTransports;
10034    }
10035
10036    // Select which transport to use for the next backup operation.
10037    public String selectBackupTransport(String transport) {
10038        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10039                "selectBackupTransport");
10040
10041        final long oldId = Binder.clearCallingIdentity();
10042        try {
10043            String prevTransport = mTransportManager.selectTransport(transport);
10044            Settings.Secure.putString(mContext.getContentResolver(),
10045                    Settings.Secure.BACKUP_TRANSPORT, transport);
10046            Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName()
10047                    + " returning " + prevTransport);
10048            return prevTransport;
10049        } finally {
10050            Binder.restoreCallingIdentity(oldId);
10051        }
10052    }
10053
10054    public void selectBackupTransportAsync(final ComponentName transport,
10055            final ISelectBackupTransportCallback listener) {
10056        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10057                "selectBackupTransportAsync");
10058
10059        final long oldId = Binder.clearCallingIdentity();
10060
10061        Slog.v(TAG, "selectBackupTransportAsync() called with transport " +
10062                transport.flattenToShortString());
10063
10064        mTransportManager.ensureTransportReady(transport, new SelectBackupTransportCallback() {
10065            @Override
10066            public void onSuccess(String transportName) {
10067                mTransportManager.selectTransport(transportName);
10068                Settings.Secure.putString(mContext.getContentResolver(),
10069                        Settings.Secure.BACKUP_TRANSPORT,
10070                        mTransportManager.getCurrentTransportName());
10071                Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString());
10072                try {
10073                    listener.onSuccess(transportName);
10074                } catch (RemoteException e) {
10075                    // Nothing to do here.
10076                }
10077            }
10078
10079            @Override
10080            public void onFailure(int reason) {
10081                Slog.v(TAG, "Failed to select transport: " + transport.flattenToShortString());
10082                try {
10083                    listener.onFailure(reason);
10084                } catch (RemoteException e) {
10085                    // Nothing to do here.
10086                }
10087            }
10088        });
10089
10090        Binder.restoreCallingIdentity(oldId);
10091    }
10092
10093    // Supply the configuration Intent for the given transport.  If the name is not one
10094    // of the available transports, or if the transport does not supply any configuration
10095    // UI, the method returns null.
10096    public Intent getConfigurationIntent(String transportName) {
10097        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10098                "getConfigurationIntent");
10099
10100        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
10101        if (transport != null) {
10102            try {
10103                final Intent intent = transport.configurationIntent();
10104                if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
10105                        + intent);
10106                return intent;
10107            } catch (Exception e) {
10108                /* fall through to return null */
10109                Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
10110            }
10111        }
10112
10113        return null;
10114    }
10115
10116    // Supply the configuration summary string for the given transport.  If the name is
10117    // not one of the available transports, or if the transport does not supply any
10118    // summary / destination string, the method can return null.
10119    //
10120    // This string is used VERBATIM as the summary text of the relevant Settings item!
10121    public String getDestinationString(String transportName) {
10122        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10123                "getDestinationString");
10124
10125        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
10126        if (transport != null) {
10127            try {
10128                final String text = transport.currentDestinationString();
10129                if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
10130                return text;
10131            } catch (Exception e) {
10132                /* fall through to return null */
10133                Slog.e(TAG, "Unable to get string from transport: " + e.getMessage());
10134            }
10135        }
10136
10137        return null;
10138    }
10139
10140    // Supply the manage-data intent for the given transport.
10141    public Intent getDataManagementIntent(String transportName) {
10142        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10143                "getDataManagementIntent");
10144
10145        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
10146        if (transport != null) {
10147            try {
10148                final Intent intent = transport.dataManagementIntent();
10149                if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
10150                        + intent);
10151                return intent;
10152            } catch (Exception e) {
10153                /* fall through to return null */
10154                Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
10155            }
10156        }
10157
10158        return null;
10159    }
10160
10161    // Supply the menu label for affordances that fire the manage-data intent
10162    // for the given transport.
10163    public String getDataManagementLabel(String transportName) {
10164        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10165                "getDataManagementLabel");
10166
10167        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
10168        if (transport != null) {
10169            try {
10170                final String text = transport.dataManagementLabel();
10171                if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
10172                return text;
10173            } catch (Exception e) {
10174                /* fall through to return null */
10175                Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
10176            }
10177        }
10178
10179        return null;
10180    }
10181
10182    // Callback: a requested backup agent has been instantiated.  This should only
10183    // be called from the Activity Manager.
10184    public void agentConnected(String packageName, IBinder agentBinder) {
10185        synchronized(mAgentConnectLock) {
10186            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
10187                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
10188                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
10189                mConnectedAgent = agent;
10190                mConnecting = false;
10191            } else {
10192                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
10193                        + " claiming agent connected");
10194            }
10195            mAgentConnectLock.notifyAll();
10196        }
10197    }
10198
10199    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
10200    // If the agent failed to come up in the first place, the agentBinder argument
10201    // will be null.  This should only be called from the Activity Manager.
10202    public void agentDisconnected(String packageName) {
10203        // TODO: handle backup being interrupted
10204        synchronized(mAgentConnectLock) {
10205            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
10206                mConnectedAgent = null;
10207                mConnecting = false;
10208            } else {
10209                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
10210                        + " claiming agent disconnected");
10211            }
10212            mAgentConnectLock.notifyAll();
10213        }
10214    }
10215
10216    // An application being installed will need a restore pass, then the Package Manager
10217    // will need to be told when the restore is finished.
10218    public void restoreAtInstall(String packageName, int token) {
10219        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
10220            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
10221                    + " attemping install-time restore");
10222            return;
10223        }
10224
10225        boolean skip = false;
10226
10227        long restoreSet = getAvailableRestoreToken(packageName);
10228        if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
10229                + " token=" + Integer.toHexString(token)
10230                + " restoreSet=" + Long.toHexString(restoreSet));
10231        if (restoreSet == 0) {
10232            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
10233            skip = true;
10234        }
10235
10236        // Do we have a transport to fetch data for us?
10237        IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
10238        if (transport == null) {
10239            if (DEBUG) Slog.w(TAG, "No transport");
10240            skip = true;
10241        }
10242
10243        if (!mAutoRestore) {
10244            if (DEBUG) {
10245                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
10246            }
10247            skip = true;
10248        }
10249
10250        if (!skip) {
10251            try {
10252                // okay, we're going to attempt a restore of this package from this restore set.
10253                // The eventual message back into the Package Manager to run the post-install
10254                // steps for 'token' will be issued from the restore handling code.
10255
10256                // This can throw and so *must* happen before the wakelock is acquired
10257                String dirName = transport.transportDirName();
10258
10259                mWakelock.acquire();
10260                if (MORE_DEBUG) {
10261                    Slog.d(TAG, "Restore at install of " + packageName);
10262                }
10263                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
10264                msg.obj = new RestoreParams(transport, dirName, null, null,
10265                        restoreSet, packageName, token);
10266                mBackupHandler.sendMessage(msg);
10267            } catch (Exception e) {
10268                // Calling into the transport broke; back off and proceed with the installation.
10269                Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
10270                skip = true;
10271            }
10272        }
10273
10274        if (skip) {
10275            // Auto-restore disabled or no way to attempt a restore; just tell the Package
10276            // Manager to proceed with the post-install handling for this package.
10277            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
10278            try {
10279                mPackageManagerBinder.finishPackageInstall(token, false);
10280            } catch (RemoteException e) { /* can't happen */ }
10281        }
10282    }
10283
10284    // Hand off a restore session
10285    public IRestoreSession beginRestoreSession(String packageName, String transport) {
10286        if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
10287                + " transport=" + transport);
10288
10289        boolean needPermission = true;
10290        if (transport == null) {
10291            transport = mTransportManager.getCurrentTransportName();
10292
10293            if (packageName != null) {
10294                PackageInfo app = null;
10295                try {
10296                    app = mPackageManager.getPackageInfo(packageName, 0);
10297                } catch (NameNotFoundException nnf) {
10298                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
10299                    throw new IllegalArgumentException("Package " + packageName + " not found");
10300                }
10301
10302                if (app.applicationInfo.uid == Binder.getCallingUid()) {
10303                    // So: using the current active transport, and the caller has asked
10304                    // that its own package will be restored.  In this narrow use case
10305                    // we do not require the caller to hold the permission.
10306                    needPermission = false;
10307                }
10308            }
10309        }
10310
10311        if (needPermission) {
10312            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10313                    "beginRestoreSession");
10314        } else {
10315            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
10316        }
10317
10318        synchronized(this) {
10319            if (mActiveRestoreSession != null) {
10320                Slog.i(TAG, "Restore session requested but one already active");
10321                return null;
10322            }
10323            if (mBackupRunning) {
10324                Slog.i(TAG, "Restore session requested but currently running backups");
10325                return null;
10326            }
10327            mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
10328            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
10329                    TIMEOUT_RESTORE_INTERVAL);
10330        }
10331        return mActiveRestoreSession;
10332    }
10333
10334    void clearRestoreSession(ActiveRestoreSession currentSession) {
10335        synchronized(this) {
10336            if (currentSession != mActiveRestoreSession) {
10337                Slog.e(TAG, "ending non-current restore session");
10338            } else {
10339                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
10340                mActiveRestoreSession = null;
10341                mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
10342            }
10343        }
10344    }
10345
10346    // Note that a currently-active backup agent has notified us that it has
10347    // completed the given outstanding asynchronous backup/restore operation.
10348    public void opComplete(int token, long result) {
10349        if (MORE_DEBUG) {
10350            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
10351        }
10352        Operation op = null;
10353        synchronized (mCurrentOpLock) {
10354            op = mCurrentOperations.get(token);
10355            if (op != null) {
10356                if (op.state == OP_TIMEOUT) {
10357                    // The operation already timed out, and this is a late response.  Tidy up
10358                    // and ignore it; we've already dealt with the timeout.
10359                    op = null;
10360                    mCurrentOperations.delete(token);
10361                } else if (op.state == OP_ACKNOWLEDGED) {
10362                    if (DEBUG) {
10363                        Slog.w(TAG, "Received duplicate ack for token=" +
10364                                Integer.toHexString(token));
10365                    }
10366                    op = null;
10367                    mCurrentOperations.remove(token);
10368                } else if (op.state == OP_PENDING) {
10369                    // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
10370                    // called after we we receive this call.
10371                    op.state = OP_ACKNOWLEDGED;
10372                }
10373            }
10374            mCurrentOpLock.notifyAll();
10375        }
10376
10377        // The completion callback, if any, is invoked on the handler
10378        if (op != null && op.callback != null) {
10379            Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
10380            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
10381            mBackupHandler.sendMessage(msg);
10382        }
10383    }
10384
10385    public boolean isAppEligibleForBackup(String packageName) {
10386        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10387                "isAppEligibleForBackup");
10388        try {
10389            PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
10390                    PackageManager.GET_SIGNATURES);
10391            if (!appIsEligibleForBackup(packageInfo.applicationInfo) ||
10392                    appIsStopped(packageInfo.applicationInfo)) {
10393                return false;
10394            }
10395            IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
10396            if (transport != null) {
10397                try {
10398                    return transport.isAppEligibleForBackup(packageInfo,
10399                        appGetsFullBackup(packageInfo));
10400                } catch (Exception e) {
10401                    Slog.e(TAG, "Unable to ask about eligibility: " + e.getMessage());
10402                }
10403            }
10404            // If transport is not present we couldn't tell that the package is not eligible.
10405            return true;
10406        } catch (NameNotFoundException e) {
10407            return false;
10408        }
10409    }
10410
10411    // ----- Restore session -----
10412
10413    class ActiveRestoreSession extends IRestoreSession.Stub {
10414        private static final String TAG = "RestoreSession";
10415
10416        private String mPackageName;
10417        private IBackupTransport mRestoreTransport = null;
10418        RestoreSet[] mRestoreSets = null;
10419        boolean mEnded = false;
10420        boolean mTimedOut = false;
10421
10422        ActiveRestoreSession(String packageName, String transport) {
10423            mPackageName = packageName;
10424            mRestoreTransport = mTransportManager.getTransportBinder(transport);
10425        }
10426
10427        public void markTimedOut() {
10428            mTimedOut = true;
10429        }
10430
10431        // --- Binder interface ---
10432        public synchronized int getAvailableRestoreSets(IRestoreObserver observer,
10433                IBackupManagerMonitor monitor) {
10434            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10435                    "getAvailableRestoreSets");
10436            if (observer == null) {
10437                throw new IllegalArgumentException("Observer must not be null");
10438            }
10439
10440            if (mEnded) {
10441                throw new IllegalStateException("Restore session already ended");
10442            }
10443
10444            if (mTimedOut) {
10445                Slog.i(TAG, "Session already timed out");
10446                return -1;
10447            }
10448
10449            long oldId = Binder.clearCallingIdentity();
10450            try {
10451                if (mRestoreTransport == null) {
10452                    Slog.w(TAG, "Null transport getting restore sets");
10453                    return -1;
10454                }
10455
10456                // We know we're doing legit work now, so halt the timeout
10457                // until we're done.  It gets started again when the result
10458                // comes in.
10459                mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
10460
10461                // spin off the transport request to our service thread
10462                mWakelock.acquire();
10463                Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
10464                        new RestoreGetSetsParams(mRestoreTransport, this, observer,
10465                                monitor));
10466                mBackupHandler.sendMessage(msg);
10467                return 0;
10468            } catch (Exception e) {
10469                Slog.e(TAG, "Error in getAvailableRestoreSets", e);
10470                return -1;
10471            } finally {
10472                Binder.restoreCallingIdentity(oldId);
10473            }
10474        }
10475
10476        public synchronized int restoreAll(long token, IRestoreObserver observer,
10477                IBackupManagerMonitor monitor) {
10478            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10479                    "performRestore");
10480
10481            if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
10482                    + " observer=" + observer);
10483
10484            if (mEnded) {
10485                throw new IllegalStateException("Restore session already ended");
10486            }
10487
10488            if (mTimedOut) {
10489                Slog.i(TAG, "Session already timed out");
10490                return -1;
10491            }
10492
10493            if (mRestoreTransport == null || mRestoreSets == null) {
10494                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
10495                return -1;
10496            }
10497
10498            if (mPackageName != null) {
10499                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
10500                return -1;
10501            }
10502
10503            String dirName;
10504            try {
10505                dirName = mRestoreTransport.transportDirName();
10506            } catch (Exception e) {
10507                // Transport went AWOL; fail.
10508                Slog.e(TAG, "Unable to get transport dir for restore: " + e.getMessage());
10509                return -1;
10510            }
10511
10512            synchronized (mQueueLock) {
10513                for (int i = 0; i < mRestoreSets.length; i++) {
10514                    if (token == mRestoreSets[i].token) {
10515                        // Real work, so stop the session timeout until we finalize the restore
10516                        mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
10517
10518                        long oldId = Binder.clearCallingIdentity();
10519                        mWakelock.acquire();
10520                        if (MORE_DEBUG) {
10521                            Slog.d(TAG, "restoreAll() kicking off");
10522                        }
10523                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
10524                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
10525                                observer, monitor, token);
10526                        mBackupHandler.sendMessage(msg);
10527                        Binder.restoreCallingIdentity(oldId);
10528                        return 0;
10529                    }
10530                }
10531            }
10532
10533            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
10534            return -1;
10535        }
10536
10537        // Restores of more than a single package are treated as 'system' restores
10538        public synchronized int restoreSome(long token, IRestoreObserver observer,
10539                IBackupManagerMonitor monitor, String[] packages) {
10540            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
10541                    "performRestore");
10542
10543            if (DEBUG) {
10544                StringBuilder b = new StringBuilder(128);
10545                b.append("restoreSome token=");
10546                b.append(Long.toHexString(token));
10547                b.append(" observer=");
10548                b.append(observer.toString());
10549                b.append(" monitor=");
10550                if (monitor == null) {
10551                    b.append("null");
10552                } else {
10553                    b.append(monitor.toString());
10554                }
10555                b.append(" packages=");
10556                if (packages == null) {
10557                    b.append("null");
10558                } else {
10559                    b.append('{');
10560                    boolean first = true;
10561                    for (String s : packages) {
10562                        if (!first) {
10563                            b.append(", ");
10564                        } else first = false;
10565                        b.append(s);
10566                    }
10567                    b.append('}');
10568                }
10569                Slog.d(TAG, b.toString());
10570            }
10571
10572            if (mEnded) {
10573                throw new IllegalStateException("Restore session already ended");
10574            }
10575
10576            if (mTimedOut) {
10577                Slog.i(TAG, "Session already timed out");
10578                return -1;
10579            }
10580
10581            if (mRestoreTransport == null || mRestoreSets == null) {
10582                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
10583                return -1;
10584            }
10585
10586            if (mPackageName != null) {
10587                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
10588                return -1;
10589            }
10590
10591            String dirName;
10592            try {
10593                dirName = mRestoreTransport.transportDirName();
10594            } catch (Exception e) {
10595                // Transport went AWOL; fail.
10596                Slog.e(TAG, "Unable to get transport name for restoreSome: " + e.getMessage());
10597                return -1;
10598            }
10599
10600            synchronized (mQueueLock) {
10601                for (int i = 0; i < mRestoreSets.length; i++) {
10602                    if (token == mRestoreSets[i].token) {
10603                        // Stop the session timeout until we finalize the restore
10604                        mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
10605
10606                        long oldId = Binder.clearCallingIdentity();
10607                        mWakelock.acquire();
10608                        if (MORE_DEBUG) {
10609                            Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
10610                        }
10611                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
10612                        msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, monitor,
10613                                token, packages, packages.length > 1);
10614                        mBackupHandler.sendMessage(msg);
10615                        Binder.restoreCallingIdentity(oldId);
10616                        return 0;
10617                    }
10618                }
10619            }
10620
10621            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
10622            return -1;
10623        }
10624
10625        public synchronized int restorePackage(String packageName, IRestoreObserver observer,
10626                IBackupManagerMonitor monitor) {
10627            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer
10628                    + "monitor=" + monitor);
10629
10630            if (mEnded) {
10631                throw new IllegalStateException("Restore session already ended");
10632            }
10633
10634            if (mTimedOut) {
10635                Slog.i(TAG, "Session already timed out");
10636                return -1;
10637            }
10638
10639            if (mPackageName != null) {
10640                if (! mPackageName.equals(packageName)) {
10641                    Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
10642                            + " on session for package " + mPackageName);
10643                    return -1;
10644                }
10645            }
10646
10647            PackageInfo app = null;
10648            try {
10649                app = mPackageManager.getPackageInfo(packageName, 0);
10650            } catch (NameNotFoundException nnf) {
10651                Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
10652                return -1;
10653            }
10654
10655            // If the caller is not privileged and is not coming from the target
10656            // app's uid, throw a permission exception back to the caller.
10657            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
10658                    Binder.getCallingPid(), Binder.getCallingUid());
10659            if ((perm == PackageManager.PERMISSION_DENIED) &&
10660                    (app.applicationInfo.uid != Binder.getCallingUid())) {
10661                Slog.w(TAG, "restorePackage: bad packageName=" + packageName
10662                        + " or calling uid=" + Binder.getCallingUid());
10663                throw new SecurityException("No permission to restore other packages");
10664            }
10665
10666            // So far so good; we're allowed to try to restore this package.
10667            long oldId = Binder.clearCallingIdentity();
10668            try {
10669                // Check whether there is data for it in the current dataset, falling back
10670                // to the ancestral dataset if not.
10671                long token = getAvailableRestoreToken(packageName);
10672                if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName
10673                        + " token=" + Long.toHexString(token));
10674
10675                // If we didn't come up with a place to look -- no ancestral dataset and
10676                // the app has never been backed up from this device -- there's nothing
10677                // to do but return failure.
10678                if (token == 0) {
10679                    if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
10680                    return -1;
10681                }
10682
10683                String dirName;
10684                try {
10685                    dirName = mRestoreTransport.transportDirName();
10686                } catch (Exception e) {
10687                    // Transport went AWOL; fail.
10688                    Slog.e(TAG, "Unable to get transport dir for restorePackage: " + e.getMessage());
10689                    return -1;
10690                }
10691
10692                // Stop the session timeout until we finalize the restore
10693                mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
10694
10695                // Ready to go:  enqueue the restore request and claim success
10696                mWakelock.acquire();
10697                if (MORE_DEBUG) {
10698                    Slog.d(TAG, "restorePackage() : " + packageName);
10699                }
10700                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
10701                msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, monitor,
10702                        token, app);
10703                mBackupHandler.sendMessage(msg);
10704            } finally {
10705                Binder.restoreCallingIdentity(oldId);
10706            }
10707            return 0;
10708        }
10709
10710        // Posted to the handler to tear down a restore session in a cleanly synchronized way
10711        class EndRestoreRunnable implements Runnable {
10712            BackupManagerService mBackupManager;
10713            ActiveRestoreSession mSession;
10714
10715            EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
10716                mBackupManager = manager;
10717                mSession = session;
10718            }
10719
10720            public void run() {
10721                // clean up the session's bookkeeping
10722                synchronized (mSession) {
10723                    mSession.mRestoreTransport = null;
10724                    mSession.mEnded = true;
10725                }
10726
10727                // clean up the BackupManagerImpl side of the bookkeeping
10728                // and cancel any pending timeout message
10729                mBackupManager.clearRestoreSession(mSession);
10730            }
10731        }
10732
10733        public synchronized void endRestoreSession() {
10734            if (DEBUG) Slog.d(TAG, "endRestoreSession");
10735
10736            if (mTimedOut) {
10737                Slog.i(TAG, "Session already timed out");
10738                return;
10739            }
10740
10741            if (mEnded) {
10742                throw new IllegalStateException("Restore session already ended");
10743            }
10744
10745            mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
10746        }
10747    }
10748
10749    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
10750        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
10751
10752        long identityToken = Binder.clearCallingIdentity();
10753        try {
10754            if (args != null) {
10755                for (String arg : args) {
10756                    if ("-h".equals(arg)) {
10757                        pw.println("'dumpsys backup' optional arguments:");
10758                        pw.println("  -h       : this help text");
10759                        pw.println("  a[gents] : dump information about defined backup agents");
10760                        return;
10761                    } else if ("agents".startsWith(arg)) {
10762                        dumpAgents(pw);
10763                        return;
10764                    }
10765                }
10766            }
10767            dumpInternal(pw);
10768        } finally {
10769            Binder.restoreCallingIdentity(identityToken);
10770        }
10771    }
10772
10773    private void dumpAgents(PrintWriter pw) {
10774        List<PackageInfo> agentPackages = allAgentPackages();
10775        pw.println("Defined backup agents:");
10776        for (PackageInfo pkg : agentPackages) {
10777            pw.print("  ");
10778            pw.print(pkg.packageName); pw.println(':');
10779            pw.print("      "); pw.println(pkg.applicationInfo.backupAgentName);
10780        }
10781    }
10782
10783    private void dumpInternal(PrintWriter pw) {
10784        synchronized (mQueueLock) {
10785            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
10786                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
10787                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
10788            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
10789            if (mBackupRunning) pw.println("Backup currently running");
10790            pw.println("Last backup pass started: " + mLastBackupPass
10791                    + " (now = " + System.currentTimeMillis() + ')');
10792            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
10793
10794            pw.println("Transport whitelist:");
10795            for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
10796                pw.print("    ");
10797                pw.println(transport.flattenToShortString());
10798            }
10799
10800            pw.println("Available transports:");
10801            final String[] transports = listAllTransports();
10802            if (transports != null) {
10803                for (String t : listAllTransports()) {
10804                    pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? "  * " : "    ") + t);
10805                    try {
10806                        IBackupTransport transport = mTransportManager.getTransportBinder(t);
10807                        File dir = new File(mBaseStateDir, transport.transportDirName());
10808                        pw.println("       destination: " + transport.currentDestinationString());
10809                        pw.println("       intent: " + transport.configurationIntent());
10810                        for (File f : dir.listFiles()) {
10811                            pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
10812                        }
10813                    } catch (Exception e) {
10814                        Slog.e(TAG, "Error in transport", e);
10815                        pw.println("        Error: " + e);
10816                    }
10817                }
10818            }
10819
10820            pw.println("Pending init: " + mPendingInits.size());
10821            for (String s : mPendingInits) {
10822                pw.println("    " + s);
10823            }
10824
10825            if (DEBUG_BACKUP_TRACE) {
10826                synchronized (mBackupTrace) {
10827                    if (!mBackupTrace.isEmpty()) {
10828                        pw.println("Most recent backup trace:");
10829                        for (String s : mBackupTrace) {
10830                            pw.println("   " + s);
10831                        }
10832                    }
10833                }
10834            }
10835
10836            pw.print("Ancestral: "); pw.println(Long.toHexString(mAncestralToken));
10837            pw.print("Current:   "); pw.println(Long.toHexString(mCurrentToken));
10838
10839            int N = mBackupParticipants.size();
10840            pw.println("Participants:");
10841            for (int i=0; i<N; i++) {
10842                int uid = mBackupParticipants.keyAt(i);
10843                pw.print("  uid: ");
10844                pw.println(uid);
10845                HashSet<String> participants = mBackupParticipants.valueAt(i);
10846                for (String app: participants) {
10847                    pw.println("    " + app);
10848                }
10849            }
10850
10851            pw.println("Ancestral packages: "
10852                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
10853            if (mAncestralPackages != null) {
10854                for (String pkg : mAncestralPackages) {
10855                    pw.println("    " + pkg);
10856                }
10857            }
10858
10859            pw.println("Ever backed up: " + mEverStoredApps.size());
10860            for (String pkg : mEverStoredApps) {
10861                pw.println("    " + pkg);
10862            }
10863
10864            pw.println("Pending key/value backup: " + mPendingBackups.size());
10865            for (BackupRequest req : mPendingBackups.values()) {
10866                pw.println("    " + req);
10867            }
10868
10869            pw.println("Full backup queue:" + mFullBackupQueue.size());
10870            for (FullBackupEntry entry : mFullBackupQueue) {
10871                pw.print("    "); pw.print(entry.lastBackup);
10872                pw.print(" : "); pw.println(entry.packageName);
10873            }
10874        }
10875    }
10876
10877    private static void sendBackupOnUpdate(IBackupObserver observer, String packageName,
10878            BackupProgress progress) {
10879        if (observer != null) {
10880            try {
10881                observer.onUpdate(packageName, progress);
10882            } catch (RemoteException e) {
10883                if (DEBUG) {
10884                    Slog.w(TAG, "Backup observer went away: onUpdate");
10885                }
10886            }
10887        }
10888    }
10889
10890    private static void sendBackupOnPackageResult(IBackupObserver observer, String packageName,
10891            int status) {
10892        if (observer != null) {
10893            try {
10894                observer.onResult(packageName, status);
10895            } catch (RemoteException e) {
10896                if (DEBUG) {
10897                    Slog.w(TAG, "Backup observer went away: onResult");
10898                }
10899            }
10900        }
10901    }
10902
10903    private static void sendBackupFinished(IBackupObserver observer, int status) {
10904        if (observer != null) {
10905            try {
10906                observer.backupFinished(status);
10907            } catch (RemoteException e) {
10908                if (DEBUG) {
10909                    Slog.w(TAG, "Backup observer went away: backupFinished");
10910                }
10911            }
10912        }
10913    }
10914
10915    private static IBackupManagerMonitor monitorEvent(IBackupManagerMonitor monitor, int id,
10916            PackageInfo pkg, int category) {
10917        if (monitor != null) {
10918            try {
10919                Bundle bundle = new Bundle();
10920                bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, id);
10921                bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, category);
10922                if (pkg != null) {
10923                    bundle.putString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME,
10924                            pkg.packageName);
10925                    bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION,
10926                            pkg.versionCode);
10927                }
10928                monitor.onEvent(bundle);
10929                return monitor;
10930            } catch(RemoteException e) {
10931                if (DEBUG) {
10932                    Slog.w(TAG, "backup manager monitor went away");
10933                }
10934            }
10935        }
10936        return null;
10937    }
10938}
10939