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