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