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