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