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