BackupManagerService.java revision 98f1ff05580f85debaa245addd02777fe9e68273
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.getMessage());
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                        // If we've lost our running criteria, tell the transport to cancel
4470                        // and roll back this (partial) backup payload; otherwise tell it
4471                        // that we've reached the clean finish state.
4472                        if (!mKeepRunning.get()) {
4473                            backupPackageStatus = BackupTransport.TRANSPORT_ERROR;
4474                            transport.cancelFullBackup();
4475                        } else {
4476                            // If we were otherwise in a good state, now interpret the final
4477                            // result based on what finishBackup() returns.  If we're in a
4478                            // failure case already, preserve that result and ignore whatever
4479                            // finishBackup() reports.
4480                            final int finishResult = transport.finishBackup();
4481                            if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
4482                                backupPackageStatus = finishResult;
4483                            }
4484                        }
4485
4486                        // A transport-originated error here means that we've hit an error that the
4487                        // runner doesn't know about, so it's still moving data but we're pulling the
4488                        // rug out from under it.  Don't ask for its result:  we already know better
4489                        // and we'll hang if we block waiting for it, since it relies on us to
4490                        // read back the data it's writing into the engine.  Just proceed with
4491                        // a graceful failure.  The runner/engine mechanism will tear itself
4492                        // down cleanly when we close the pipes from this end.  Transport-level
4493                        // errors take precedence over agent/app-specific errors for purposes of
4494                        // determining our course of action.
4495                        if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
4496                            // We still could fail in backup runner thread, getting result from there.
4497                            int backupRunnerResult = backupRunner.getBackupResultBlocking();
4498                            if (backupRunnerResult != BackupTransport.TRANSPORT_OK) {
4499                                // If there was an error in runner thread and
4500                                // not TRANSPORT_ERROR here, overwrite it.
4501                                backupPackageStatus = backupRunnerResult;
4502                            }
4503                        } else {
4504                            if (MORE_DEBUG) {
4505                                Slog.i(TAG, "Transport-level failure; cancelling agent work");
4506                            }
4507                        }
4508
4509                        if (MORE_DEBUG) {
4510                            Slog.i(TAG, "Done delivering backup data: result="
4511                                    + backupPackageStatus);
4512                        }
4513
4514                        if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
4515                            Slog.e(TAG, "Error " + backupPackageStatus + " backing up "
4516                                    + packageName);
4517                        }
4518
4519                        // Also ask the transport how long it wants us to wait before
4520                        // moving on to the next package, if any.
4521                        backoff = transport.requestFullBackupTime();
4522                        if (DEBUG_SCHEDULING) {
4523                            Slog.i(TAG, "Transport suggested backoff=" + backoff);
4524                        }
4525
4526                    }
4527
4528                    // Roll this package to the end of the backup queue if we're
4529                    // in a queue-driven mode (regardless of success/failure)
4530                    if (mUpdateSchedule) {
4531                        enqueueFullBackup(packageName, System.currentTimeMillis());
4532                    }
4533
4534                    if (backupPackageStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
4535                        sendBackupOnPackageResult(mBackupObserver, packageName,
4536                                BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
4537                        if (DEBUG) {
4538                            Slog.i(TAG, "Transport rejected backup of " + packageName
4539                                    + ", skipping");
4540                        }
4541                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName,
4542                                "transport rejected");
4543                        // Do nothing, clean up, and continue looping.
4544                    } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
4545                        sendBackupOnPackageResult(mBackupObserver, packageName,
4546                                BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
4547                        if (DEBUG) {
4548                            Slog.i(TAG, "Transport quota exceeded for package: " + packageName);
4549                            EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
4550                                    packageName);
4551                        }
4552                        // Do nothing, clean up, and continue looping.
4553                    } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
4554                        sendBackupOnPackageResult(mBackupObserver, packageName,
4555                                BackupManager.ERROR_AGENT_FAILURE);
4556                        Slog.w(TAG, "Application failure for package: " + packageName);
4557                        EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
4558                        // Do nothing, clean up, and continue looping.
4559                    } else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
4560                        sendBackupOnPackageResult(mBackupObserver, packageName,
4561                            BackupManager.ERROR_TRANSPORT_ABORTED);
4562                        Slog.w(TAG, "Transport failed; aborting backup: " + backupPackageStatus);
4563                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
4564                        // Abort entire backup pass.
4565                        backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
4566                        return;
4567                    } else {
4568                        // Success!
4569                        sendBackupOnPackageResult(mBackupObserver, packageName,
4570                                BackupManager.SUCCESS);
4571                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, packageName);
4572                        logBackupComplete(packageName);
4573                    }
4574                    cleanUpPipes(transportPipes);
4575                    cleanUpPipes(enginePipes);
4576                }
4577            } catch (Exception e) {
4578                backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
4579                Slog.w(TAG, "Exception trying full transport backup", e);
4580            } finally {
4581                if (DEBUG) {
4582                    Slog.i(TAG, "Full backup completed with status: " + backupRunStatus);
4583                }
4584                sendBackupFinished(mBackupObserver, backupRunStatus);
4585
4586                cleanUpPipes(transportPipes);
4587                cleanUpPipes(enginePipes);
4588
4589                if (mJob != null) {
4590                    mJob.finishBackupPass();
4591                }
4592
4593                synchronized (mQueueLock) {
4594                    mRunningFullBackupTask = null;
4595                }
4596
4597
4598                mLatch.countDown();
4599
4600                // Now that we're actually done with schedule-driven work, reschedule
4601                // the next pass based on the new queue state.
4602                if (mUpdateSchedule) {
4603                    scheduleNextFullBackupJob(backoff);
4604                }
4605                Slog.i(BackupManagerService.TAG, "Full data backup pass finished.");
4606                mWakelock.release();
4607            }
4608        }
4609
4610        void cleanUpPipes(ParcelFileDescriptor[] pipes) {
4611            if (pipes != null) {
4612                if (pipes[0] != null) {
4613                    ParcelFileDescriptor fd = pipes[0];
4614                    pipes[0] = null;
4615                    try {
4616                        fd.close();
4617                    } catch (IOException e) {
4618                        Slog.w(TAG, "Unable to close pipe!");
4619                    }
4620                }
4621                if (pipes[1] != null) {
4622                    ParcelFileDescriptor fd = pipes[1];
4623                    pipes[1] = null;
4624                    try {
4625                        fd.close();
4626                    } catch (IOException e) {
4627                        Slog.w(TAG, "Unable to close pipe!");
4628                    }
4629                }
4630            }
4631        }
4632
4633        // Run the backup and pipe it back to the given socket -- expects to run on
4634        // a standalone thread.  The  runner owns this half of the pipe, and closes
4635        // it to indicate EOD to the other end.
4636        class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
4637            final AtomicLong mResult = new AtomicLong();
4638            final CountDownLatch mLatch = new CountDownLatch(1);
4639            final IBackupTransport mTransport;
4640
4641            public SinglePackageBackupPreflight(IBackupTransport transport) {
4642                mTransport = transport;
4643            }
4644
4645            @Override
4646            public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
4647                int result;
4648                try {
4649                    final int token = generateToken();
4650                    prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this);
4651                    addBackupTrace("preflighting");
4652                    if (MORE_DEBUG) {
4653                        Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
4654                    }
4655                    agent.doMeasureFullBackup(token, mBackupManagerBinder);
4656
4657                    // now wait to get our result back
4658                    mLatch.await();
4659                    long totalSize = mResult.get();
4660                    // If preflight timeouted, mResult will contain error code as int.
4661                    if (totalSize < 0) {
4662                        return (int) totalSize;
4663                    }
4664                    if (MORE_DEBUG) {
4665                        Slog.v(TAG, "Got preflight response; size=" + totalSize);
4666                    }
4667
4668                    result = mTransport.checkFullBackupSize(totalSize);
4669                    if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
4670                        final long quota = mTransport.getBackupQuota(pkg.packageName, true);
4671                        if (MORE_DEBUG) {
4672                            Slog.d(TAG, "Package hit quota limit on preflight " +
4673                                    pkg.packageName + ": " + totalSize + " of " + quota);
4674                        }
4675                        agent.doQuotaExceeded(totalSize, quota);
4676                    }
4677                } catch (Exception e) {
4678                    Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
4679                    result = BackupTransport.AGENT_ERROR;
4680                }
4681                return result;
4682            }
4683
4684            @Override
4685            public void execute() {
4686                // Unused in this case
4687            }
4688
4689            @Override
4690            public void operationComplete(long result) {
4691                // got the callback, and our preflightFullBackup() method is waiting for the result
4692                if (MORE_DEBUG) {
4693                    Slog.i(TAG, "Preflight op complete, result=" + result);
4694                }
4695                mResult.set(result);
4696                mLatch.countDown();
4697            }
4698
4699            @Override
4700            public void handleTimeout() {
4701                if (MORE_DEBUG) {
4702                    Slog.i(TAG, "Preflight timeout; failing");
4703                }
4704                mResult.set(BackupTransport.AGENT_ERROR);
4705                mLatch.countDown();
4706            }
4707
4708            @Override
4709            public long getExpectedSizeOrErrorCode() {
4710                try {
4711                    mLatch.await();
4712                    return mResult.get();
4713                } catch (InterruptedException e) {
4714                    return BackupTransport.NO_MORE_DATA;
4715                }
4716            }
4717        }
4718
4719        class SinglePackageBackupRunner implements Runnable {
4720            final ParcelFileDescriptor mOutput;
4721            final PackageInfo mTarget;
4722            final FullBackupPreflight mPreflight;
4723            final CountDownLatch mPreflightLatch;
4724            final CountDownLatch mBackupLatch;
4725            private FullBackupEngine mEngine;
4726            private volatile int mPreflightResult;
4727            private volatile int mBackupResult;
4728
4729            SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
4730                    IBackupTransport transport) throws IOException {
4731                mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
4732                mTarget = target;
4733                mPreflight = new SinglePackageBackupPreflight(transport);
4734                mPreflightLatch = new CountDownLatch(1);
4735                mBackupLatch = new CountDownLatch(1);
4736                mPreflightResult = BackupTransport.TRANSPORT_OK;
4737                mBackupResult = BackupTransport.TRANSPORT_OK;
4738            }
4739
4740            @Override
4741            public void run() {
4742                FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
4743                mEngine = new FullBackupEngine(out, mPreflight, mTarget, false);
4744                try {
4745                    try {
4746                        mPreflightResult = mEngine.preflightCheck();
4747                    } finally {
4748                        mPreflightLatch.countDown();
4749                    }
4750                    // If there is no error on preflight, continue backup.
4751                    if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
4752                        mBackupResult = mEngine.backupOnePackage();
4753                    }
4754                } catch (Exception e) {
4755                    Slog.e(TAG, "Exception during full package backup of " + mTarget.packageName);
4756                } finally {
4757                    mBackupLatch.countDown();
4758                    try {
4759                        mOutput.close();
4760                    } catch (IOException e) {
4761                        Slog.w(TAG, "Error closing transport pipe in runner");
4762                    }
4763                }
4764            }
4765
4766            public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
4767                mEngine.sendQuotaExceeded(backupDataBytes, quotaBytes);
4768            }
4769
4770            // If preflight succeeded, returns positive number - preflight size,
4771            // otherwise return negative error code.
4772            long getPreflightResultBlocking() {
4773                try {
4774                    mPreflightLatch.await();
4775                    if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
4776                        return mPreflight.getExpectedSizeOrErrorCode();
4777                    } else {
4778                        return mPreflightResult;
4779                    }
4780                } catch (InterruptedException e) {
4781                    return BackupTransport.AGENT_ERROR;
4782                }
4783            }
4784
4785            int getBackupResultBlocking() {
4786                try {
4787                    mBackupLatch.await();
4788                    return mBackupResult;
4789                } catch (InterruptedException e) {
4790                    return BackupTransport.AGENT_ERROR;
4791                }
4792            }
4793        }
4794    }
4795
4796    // ----- Full-data backup scheduling -----
4797
4798    /**
4799     * Schedule a job to tell us when it's a good time to run a full backup
4800     */
4801    void scheduleNextFullBackupJob(long transportMinLatency) {
4802        synchronized (mQueueLock) {
4803            if (mFullBackupQueue.size() > 0) {
4804                // schedule the next job at the point in the future when the least-recently
4805                // backed up app comes due for backup again; or immediately if it's already
4806                // due.
4807                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
4808                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
4809                final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
4810                        ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
4811                final long latency = Math.max(transportMinLatency, appLatency);
4812                Runnable r = new Runnable() {
4813                    @Override public void run() {
4814                        FullBackupJob.schedule(mContext, latency);
4815                    }
4816                };
4817                mBackupHandler.postDelayed(r, 2500);
4818            } else {
4819                if (DEBUG_SCHEDULING) {
4820                    Slog.i(TAG, "Full backup queue empty; not scheduling");
4821                }
4822            }
4823        }
4824    }
4825
4826    /**
4827     * Remove a package from the full-data queue.
4828     */
4829    void dequeueFullBackupLocked(String packageName) {
4830        final int N = mFullBackupQueue.size();
4831        for (int i = N-1; i >= 0; i--) {
4832            final FullBackupEntry e = mFullBackupQueue.get(i);
4833            if (packageName.equals(e.packageName)) {
4834                mFullBackupQueue.remove(i);
4835            }
4836        }
4837    }
4838
4839    /**
4840     * Enqueue full backup for the given app, with a note about when it last ran.
4841     */
4842    void enqueueFullBackup(String packageName, long lastBackedUp) {
4843        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
4844        synchronized (mQueueLock) {
4845            // First, sanity check that we aren't adding a duplicate.  Slow but
4846            // straightforward; we'll have at most on the order of a few hundred
4847            // items in this list.
4848            dequeueFullBackupLocked(packageName);
4849
4850            // This is also slow but easy for modest numbers of apps: work backwards
4851            // from the end of the queue until we find an item whose last backup
4852            // time was before this one, then insert this new entry after it.  If we're
4853            // adding something new we don't bother scanning, and just prepend.
4854            int which = -1;
4855            if (lastBackedUp > 0) {
4856                for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
4857                    final FullBackupEntry entry = mFullBackupQueue.get(which);
4858                    if (entry.lastBackup <= lastBackedUp) {
4859                        mFullBackupQueue.add(which + 1, newEntry);
4860                        break;
4861                    }
4862                }
4863            }
4864            if (which < 0) {
4865                // this one is earlier than any existing one, so prepend
4866                mFullBackupQueue.add(0, newEntry);
4867            }
4868        }
4869        writeFullBackupScheduleAsync();
4870    }
4871
4872    private boolean fullBackupAllowable(IBackupTransport transport) {
4873        if (transport == null) {
4874            Slog.w(TAG, "Transport not present; full data backup not performed");
4875            return false;
4876        }
4877
4878        // Don't proceed unless we have already established package metadata
4879        // for the current dataset via a key/value backup pass.
4880        try {
4881            File stateDir = new File(mBaseStateDir, transport.transportDirName());
4882            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
4883            if (pmState.length() <= 0) {
4884                if (DEBUG) {
4885                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
4886                }
4887                return false;
4888            }
4889        } catch (Exception e) {
4890            Slog.w(TAG, "Unable to contact transport");
4891            return false;
4892        }
4893
4894        return true;
4895    }
4896
4897    /**
4898     * Conditions are right for a full backup operation, so run one.  The model we use is
4899     * to perform one app backup per scheduled job execution, and to reschedule the job
4900     * with zero latency as long as conditions remain right and we still have work to do.
4901     *
4902     * <p>This is the "start a full backup operation" entry point called by the scheduled job.
4903     *
4904     * @return Whether ongoing work will continue.  The return value here will be passed
4905     *         along as the return value to the scheduled job's onStartJob() callback.
4906     */
4907    boolean beginFullBackup(FullBackupJob scheduledJob) {
4908        long now = System.currentTimeMillis();
4909        FullBackupEntry entry = null;
4910        long latency = MIN_FULL_BACKUP_INTERVAL;
4911
4912        if (!mEnabled || !mProvisioned) {
4913            // Backups are globally disabled, so don't proceed.  We also don't reschedule
4914            // the job driving automatic backups; that job will be scheduled again when
4915            // the user enables backup.
4916            if (MORE_DEBUG) {
4917                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
4918                        + " p=" + mProvisioned + "; ignoring");
4919            }
4920            return false;
4921        }
4922
4923        // Don't run the backup if we're in battery saver mode, but reschedule
4924        // to try again in the not-so-distant future.
4925        if (mPowerManager.isPowerSaveMode()) {
4926            if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
4927            FullBackupJob.schedule(mContext, KeyValueBackupJob.BATCH_INTERVAL);
4928            return false;
4929        }
4930
4931        if (DEBUG_SCHEDULING) {
4932            Slog.i(TAG, "Beginning scheduled full backup operation");
4933        }
4934
4935        // Great; we're able to run full backup jobs now.  See if we have any work to do.
4936        synchronized (mQueueLock) {
4937            if (mRunningFullBackupTask != null) {
4938                Slog.e(TAG, "Backup triggered but one already/still running!");
4939                return false;
4940            }
4941
4942            // At this point we think that we have work to do, but possibly not right now.
4943            // Any exit without actually running backups will also require that we
4944            // reschedule the job.
4945            boolean runBackup = true;
4946            boolean headBusy;
4947
4948            do {
4949                // Recheck each time, because culling due to ineligibility may
4950                // have emptied the queue.
4951                if (mFullBackupQueue.size() == 0) {
4952                    // no work to do so just bow out
4953                    if (DEBUG) {
4954                        Slog.i(TAG, "Backup queue empty; doing nothing");
4955                    }
4956                    runBackup = false;
4957                    break;
4958                }
4959
4960                headBusy = false;
4961
4962                if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
4963                    if (MORE_DEBUG) {
4964                        Slog.i(TAG, "Preconditions not met; not running full backup");
4965                    }
4966                    runBackup = false;
4967                    // Typically this means we haven't run a key/value backup yet.  Back off
4968                    // full-backup operations by the key/value job's run interval so that
4969                    // next time we run, we are likely to be able to make progress.
4970                    latency = KeyValueBackupJob.BATCH_INTERVAL;
4971                }
4972
4973                if (runBackup) {
4974                    entry = mFullBackupQueue.get(0);
4975                    long timeSinceRun = now - entry.lastBackup;
4976                    runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
4977                    if (!runBackup) {
4978                        // It's too early to back up the next thing in the queue, so bow out
4979                        if (MORE_DEBUG) {
4980                            Slog.i(TAG, "Device ready but too early to back up next app");
4981                        }
4982                        // Wait until the next app in the queue falls due for a full data backup
4983                        latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
4984                        break;  // we know we aren't doing work yet, so bail.
4985                    }
4986
4987                    try {
4988                        PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
4989                        if (!appGetsFullBackup(appInfo)) {
4990                            // The head app isn't supposed to get full-data backups [any more];
4991                            // so we cull it and force a loop around to consider the new head
4992                            // app.
4993                            if (MORE_DEBUG) {
4994                                Slog.i(TAG, "Culling package " + entry.packageName
4995                                        + " in full-backup queue but not eligible");
4996                            }
4997                            mFullBackupQueue.remove(0);
4998                            headBusy = true; // force the while() condition
4999                            continue;
5000                        }
5001
5002                        final int privFlags = appInfo.applicationInfo.privateFlags;
5003                        headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
5004                                && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
5005
5006                        if (headBusy) {
5007                            final long nextEligible = System.currentTimeMillis()
5008                                    + BUSY_BACKOFF_MIN_MILLIS
5009                                    + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
5010                            if (DEBUG_SCHEDULING) {
5011                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
5012                                Slog.i(TAG, "Full backup time but " + entry.packageName
5013                                        + " is busy; deferring to "
5014                                        + sdf.format(new Date(nextEligible)));
5015                            }
5016                            // This relocates the app's entry from the head of the queue to
5017                            // its order-appropriate position further down, so upon looping
5018                            // a new candidate will be considered at the head.
5019                            enqueueFullBackup(entry.packageName,
5020                                    nextEligible - MIN_FULL_BACKUP_INTERVAL);
5021                        }
5022                    } catch (NameNotFoundException nnf) {
5023                        // So, we think we want to back this up, but it turns out the package
5024                        // in question is no longer installed.  We want to drop it from the
5025                        // queue entirely and move on, but if there's nothing else in the queue
5026                        // we should bail entirely.  headBusy cannot have been set to true yet.
5027                        runBackup = (mFullBackupQueue.size() > 1);
5028                    } catch (RemoteException e) {
5029                        // Cannot happen; the Activity Manager is in the same process
5030                    }
5031                }
5032            } while (headBusy);
5033
5034            if (!runBackup) {
5035                if (DEBUG_SCHEDULING) {
5036                    Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
5037                }
5038                final long deferTime = latency;     // pin for the closure
5039                mBackupHandler.post(new Runnable() {
5040                    @Override public void run() {
5041                        FullBackupJob.schedule(mContext, deferTime);
5042                    }
5043                });
5044                return false;
5045            }
5046
5047            // Okay, the top thing is ready for backup now.  Do it.
5048            mFullBackupQueue.remove(0);
5049            CountDownLatch latch = new CountDownLatch(1);
5050            String[] pkg = new String[] {entry.packageName};
5051            mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
5052                    scheduledJob, latch, null, false /* userInitiated */);
5053            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
5054            mWakelock.acquire();
5055            (new Thread(mRunningFullBackupTask)).start();
5056        }
5057
5058        return true;
5059    }
5060
5061    // The job scheduler says our constraints don't hold any more,
5062    // so tear down any ongoing backup task right away.
5063    void endFullBackup() {
5064        synchronized (mQueueLock) {
5065            if (mRunningFullBackupTask != null) {
5066                if (DEBUG_SCHEDULING) {
5067                    Slog.i(TAG, "Telling running backup to stop");
5068                }
5069                mRunningFullBackupTask.setRunning(false);
5070            }
5071        }
5072    }
5073
5074    // ----- Restore infrastructure -----
5075
5076    abstract class RestoreEngine {
5077        static final String TAG = "RestoreEngine";
5078
5079        public static final int SUCCESS = 0;
5080        public static final int TARGET_FAILURE = -2;
5081        public static final int TRANSPORT_FAILURE = -3;
5082
5083        private AtomicBoolean mRunning = new AtomicBoolean(false);
5084        private AtomicInteger mResult = new AtomicInteger(SUCCESS);
5085
5086        public boolean isRunning() {
5087            return mRunning.get();
5088        }
5089
5090        public void setRunning(boolean stillRunning) {
5091            synchronized (mRunning) {
5092                mRunning.set(stillRunning);
5093                mRunning.notifyAll();
5094            }
5095        }
5096
5097        public int waitForResult() {
5098            synchronized (mRunning) {
5099                while (isRunning()) {
5100                    try {
5101                        mRunning.wait();
5102                    } catch (InterruptedException e) {}
5103                }
5104            }
5105            return getResult();
5106        }
5107
5108        public int getResult() {
5109            return mResult.get();
5110        }
5111
5112        public void setResult(int result) {
5113            mResult.set(result);
5114        }
5115
5116        // TODO: abstract restore state and APIs
5117    }
5118
5119    // ----- Full restore from a file/socket -----
5120
5121    // Description of a file in the restore datastream
5122    static class FileMetadata {
5123        String packageName;             // name of the owning app
5124        String installerPackageName;    // name of the market-type app that installed the owner
5125        int type;                       // e.g. BackupAgent.TYPE_DIRECTORY
5126        String domain;                  // e.g. FullBackup.DATABASE_TREE_TOKEN
5127        String path;                    // subpath within the semantic domain
5128        long mode;                      // e.g. 0666 (actually int)
5129        long mtime;                     // last mod time, UTC time_t (actually int)
5130        long size;                      // bytes of content
5131
5132        @Override
5133        public String toString() {
5134            StringBuilder sb = new StringBuilder(128);
5135            sb.append("FileMetadata{");
5136            sb.append(packageName); sb.append(',');
5137            sb.append(type); sb.append(',');
5138            sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
5139            sb.append(size);
5140            sb.append('}');
5141            return sb.toString();
5142        }
5143    }
5144
5145    enum RestorePolicy {
5146        IGNORE,
5147        ACCEPT,
5148        ACCEPT_IF_APK
5149    }
5150
5151    // Full restore engine, used by both adb restore and transport-based full restore
5152    class FullRestoreEngine extends RestoreEngine {
5153        // Dedicated observer, if any
5154        IFullBackupRestoreObserver mObserver;
5155
5156        // Where we're delivering the file data as we go
5157        IBackupAgent mAgent;
5158
5159        // Are we permitted to only deliver a specific package's metadata?
5160        PackageInfo mOnlyPackage;
5161
5162        boolean mAllowApks;
5163        boolean mAllowObbs;
5164
5165        // Which package are we currently handling data for?
5166        String mAgentPackage;
5167
5168        // Info for working with the target app process
5169        ApplicationInfo mTargetApp;
5170
5171        // Machinery for restoring OBBs
5172        FullBackupObbConnection mObbConnection = null;
5173
5174        // possible handling states for a given package in the restore dataset
5175        final HashMap<String, RestorePolicy> mPackagePolicies
5176                = new HashMap<String, RestorePolicy>();
5177
5178        // installer package names for each encountered app, derived from the manifests
5179        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
5180
5181        // Signatures for a given package found in its manifest file
5182        final HashMap<String, Signature[]> mManifestSignatures
5183                = new HashMap<String, Signature[]>();
5184
5185        // Packages we've already wiped data on when restoring their first file
5186        final HashSet<String> mClearedPackages = new HashSet<String>();
5187
5188        // How much data have we moved?
5189        long mBytes;
5190
5191        // Working buffer
5192        byte[] mBuffer;
5193
5194        // Pipes for moving data
5195        ParcelFileDescriptor[] mPipes = null;
5196
5197        // Widget blob to be restored out-of-band
5198        byte[] mWidgetData = null;
5199
5200        // Runner that can be placed in a separate thread to do in-process
5201        // invocations of the full restore API asynchronously
5202        class RestoreFileRunnable implements Runnable {
5203            IBackupAgent mAgent;
5204            FileMetadata mInfo;
5205            ParcelFileDescriptor mSocket;
5206            int mToken;
5207
5208            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
5209                    ParcelFileDescriptor socket, int token) throws IOException {
5210                mAgent = agent;
5211                mInfo = info;
5212                mToken = token;
5213
5214                // This class is used strictly for process-local binder invocations.  The
5215                // semantics of ParcelFileDescriptor differ in this case; in particular, we
5216                // do not automatically get a 'dup'ed descriptor that we can can continue
5217                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
5218                // before proceeding to do the restore.
5219                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
5220            }
5221
5222            @Override
5223            public void run() {
5224                try {
5225                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
5226                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
5227                            mToken, mBackupManagerBinder);
5228                } catch (RemoteException e) {
5229                    // never happens; this is used strictly for local binder calls
5230                }
5231            }
5232        }
5233
5234        public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage,
5235                boolean allowApks, boolean allowObbs) {
5236            mObserver = observer;
5237            mOnlyPackage = onlyPackage;
5238            mAllowApks = allowApks;
5239            mAllowObbs = allowObbs;
5240            mBuffer = new byte[32 * 1024];
5241            mBytes = 0;
5242        }
5243
5244        public IBackupAgent getAgent() {
5245            return mAgent;
5246        }
5247
5248        public boolean restoreOneFile(InputStream instream, boolean mustKillAgent) {
5249            if (!isRunning()) {
5250                Slog.w(TAG, "Restore engine used after halting");
5251                return false;
5252            }
5253
5254            FileMetadata info;
5255            try {
5256                if (MORE_DEBUG) {
5257                    Slog.v(TAG, "Reading tar header for restoring file");
5258                }
5259                info = readTarHeaders(instream);
5260                if (info != null) {
5261                    if (MORE_DEBUG) {
5262                        dumpFileMetadata(info);
5263                    }
5264
5265                    final String pkg = info.packageName;
5266                    if (!pkg.equals(mAgentPackage)) {
5267                        // In the single-package case, it's a semantic error to expect
5268                        // one app's data but see a different app's on the wire
5269                        if (mOnlyPackage != null) {
5270                            if (!pkg.equals(mOnlyPackage.packageName)) {
5271                                Slog.w(TAG, "Expected data for " + mOnlyPackage
5272                                        + " but saw " + pkg);
5273                                setResult(RestoreEngine.TRANSPORT_FAILURE);
5274                                setRunning(false);
5275                                return false;
5276                            }
5277                        }
5278
5279                        // okay, change in package; set up our various
5280                        // bookkeeping if we haven't seen it yet
5281                        if (!mPackagePolicies.containsKey(pkg)) {
5282                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5283                        }
5284
5285                        // Clean up the previous agent relationship if necessary,
5286                        // and let the observer know we're considering a new app.
5287                        if (mAgent != null) {
5288                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
5289                            // Now we're really done
5290                            tearDownPipes();
5291                            tearDownAgent(mTargetApp);
5292                            mTargetApp = null;
5293                            mAgentPackage = null;
5294                        }
5295                    }
5296
5297                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
5298                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
5299                        mPackageInstallers.put(pkg, info.installerPackageName);
5300                        // We've read only the manifest content itself at this point,
5301                        // so consume the footer before looping around to the next
5302                        // input file
5303                        skipTarPadding(info.size, instream);
5304                        sendOnRestorePackage(pkg);
5305                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
5306                        // Metadata blobs!
5307                        readMetadata(info, instream);
5308                        skipTarPadding(info.size, instream);
5309                    } else {
5310                        // Non-manifest, so it's actual file data.  Is this a package
5311                        // we're ignoring?
5312                        boolean okay = true;
5313                        RestorePolicy policy = mPackagePolicies.get(pkg);
5314                        switch (policy) {
5315                            case IGNORE:
5316                                okay = false;
5317                                break;
5318
5319                            case ACCEPT_IF_APK:
5320                                // If we're in accept-if-apk state, then the first file we
5321                                // see MUST be the apk.
5322                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5323                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
5324                                    // Try to install the app.
5325                                    String installerName = mPackageInstallers.get(pkg);
5326                                    okay = installApk(info, installerName, instream);
5327                                    // good to go; promote to ACCEPT
5328                                    mPackagePolicies.put(pkg, (okay)
5329                                            ? RestorePolicy.ACCEPT
5330                                                    : RestorePolicy.IGNORE);
5331                                    // At this point we've consumed this file entry
5332                                    // ourselves, so just strip the tar footer and
5333                                    // go on to the next file in the input stream
5334                                    skipTarPadding(info.size, instream);
5335                                    return true;
5336                                } else {
5337                                    // File data before (or without) the apk.  We can't
5338                                    // handle it coherently in this case so ignore it.
5339                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5340                                    okay = false;
5341                                }
5342                                break;
5343
5344                            case ACCEPT:
5345                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5346                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
5347                                    // we can take the data without the apk, so we
5348                                    // *want* to do so.  skip the apk by declaring this
5349                                    // one file not-okay without changing the restore
5350                                    // policy for the package.
5351                                    okay = false;
5352                                }
5353                                break;
5354
5355                            default:
5356                                // Something has gone dreadfully wrong when determining
5357                                // the restore policy from the manifest.  Ignore the
5358                                // rest of this package's data.
5359                                Slog.e(TAG, "Invalid policy from manifest");
5360                                okay = false;
5361                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5362                                break;
5363                        }
5364
5365                        // Is it a *file* we need to drop?
5366                        if (!isRestorableFile(info)) {
5367                            okay = false;
5368                        }
5369
5370                        // If the policy is satisfied, go ahead and set up to pipe the
5371                        // data to the agent.
5372                        if (DEBUG && okay && mAgent != null) {
5373                            Slog.i(TAG, "Reusing existing agent instance");
5374                        }
5375                        if (okay && mAgent == null) {
5376                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
5377
5378                            try {
5379                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
5380
5381                                // If we haven't sent any data to this app yet, we probably
5382                                // need to clear it first.  Check that.
5383                                if (!mClearedPackages.contains(pkg)) {
5384                                    // apps with their own backup agents are
5385                                    // responsible for coherently managing a full
5386                                    // restore.
5387                                    if (mTargetApp.backupAgentName == null) {
5388                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
5389                                        clearApplicationDataSynchronous(pkg);
5390                                    } else {
5391                                        if (MORE_DEBUG) Slog.d(TAG, "backup agent ("
5392                                                + mTargetApp.backupAgentName + ") => no clear");
5393                                    }
5394                                    mClearedPackages.add(pkg);
5395                                } else {
5396                                    if (MORE_DEBUG) {
5397                                        Slog.d(TAG, "We've initialized this app already; no clear required");
5398                                    }
5399                                }
5400
5401                                // All set; now set up the IPC and launch the agent
5402                                setUpPipes();
5403                                mAgent = bindToAgentSynchronous(mTargetApp,
5404                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
5405                                mAgentPackage = pkg;
5406                            } catch (IOException e) {
5407                                // fall through to error handling
5408                            } catch (NameNotFoundException e) {
5409                                // fall through to error handling
5410                            }
5411
5412                            if (mAgent == null) {
5413                                Slog.e(TAG, "Unable to create agent for " + pkg);
5414                                okay = false;
5415                                tearDownPipes();
5416                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5417                            }
5418                        }
5419
5420                        // Sanity check: make sure we never give data to the wrong app.  This
5421                        // should never happen but a little paranoia here won't go amiss.
5422                        if (okay && !pkg.equals(mAgentPackage)) {
5423                            Slog.e(TAG, "Restoring data for " + pkg
5424                                    + " but agent is for " + mAgentPackage);
5425                            okay = false;
5426                        }
5427
5428                        // At this point we have an agent ready to handle the full
5429                        // restore data as well as a pipe for sending data to
5430                        // that agent.  Tell the agent to start reading from the
5431                        // pipe.
5432                        if (okay) {
5433                            boolean agentSuccess = true;
5434                            long toCopy = info.size;
5435                            final int token = generateToken();
5436                            try {
5437                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
5438                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
5439                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
5440                                            + " : " + info.path);
5441                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
5442                                            info.size, info.type, info.path, info.mode,
5443                                            info.mtime, token, mBackupManagerBinder);
5444                                } else {
5445                                    if (MORE_DEBUG) Slog.d(TAG, "Invoking agent to restore file "
5446                                            + info.path);
5447                                    // fire up the app's agent listening on the socket.  If
5448                                    // the agent is running in the system process we can't
5449                                    // just invoke it asynchronously, so we provide a thread
5450                                    // for it here.
5451                                    if (mTargetApp.processName.equals("system")) {
5452                                        Slog.d(TAG, "system process agent - spinning a thread");
5453                                        RestoreFileRunnable runner = new RestoreFileRunnable(
5454                                                mAgent, info, mPipes[0], token);
5455                                        new Thread(runner, "restore-sys-runner").start();
5456                                    } else {
5457                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
5458                                                info.domain, info.path, info.mode, info.mtime,
5459                                                token, mBackupManagerBinder);
5460                                    }
5461                                }
5462                            } catch (IOException e) {
5463                                // couldn't dup the socket for a process-local restore
5464                                Slog.d(TAG, "Couldn't establish restore");
5465                                agentSuccess = false;
5466                                okay = false;
5467                            } catch (RemoteException e) {
5468                                // whoops, remote entity went away.  We'll eat the content
5469                                // ourselves, then, and not copy it over.
5470                                Slog.e(TAG, "Agent crashed during full restore");
5471                                agentSuccess = false;
5472                                okay = false;
5473                            }
5474
5475                            // Copy over the data if the agent is still good
5476                            if (okay) {
5477                                if (MORE_DEBUG) {
5478                                    Slog.v(TAG, "  copying to restore agent: "
5479                                            + toCopy + " bytes");
5480                                }
5481                                boolean pipeOkay = true;
5482                                FileOutputStream pipe = new FileOutputStream(
5483                                        mPipes[1].getFileDescriptor());
5484                                while (toCopy > 0) {
5485                                    int toRead = (toCopy > mBuffer.length)
5486                                            ? mBuffer.length : (int)toCopy;
5487                                    int nRead = instream.read(mBuffer, 0, toRead);
5488                                    if (nRead >= 0) mBytes += nRead;
5489                                    if (nRead <= 0) break;
5490                                    toCopy -= nRead;
5491
5492                                    // send it to the output pipe as long as things
5493                                    // are still good
5494                                    if (pipeOkay) {
5495                                        try {
5496                                            pipe.write(mBuffer, 0, nRead);
5497                                        } catch (IOException e) {
5498                                            Slog.e(TAG, "Failed to write to restore pipe", e);
5499                                            pipeOkay = false;
5500                                        }
5501                                    }
5502                                }
5503
5504                                // done sending that file!  Now we just need to consume
5505                                // the delta from info.size to the end of block.
5506                                skipTarPadding(info.size, instream);
5507
5508                                // and now that we've sent it all, wait for the remote
5509                                // side to acknowledge receipt
5510                                agentSuccess = waitUntilOperationComplete(token);
5511                            }
5512
5513                            // okay, if the remote end failed at any point, deal with
5514                            // it by ignoring the rest of the restore on it
5515                            if (!agentSuccess) {
5516                                Slog.w(TAG, "Agent failure; ending restore");
5517                                mBackupHandler.removeMessages(MSG_TIMEOUT);
5518                                tearDownPipes();
5519                                tearDownAgent(mTargetApp);
5520                                mAgent = null;
5521                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5522
5523                                // If this was a single-package restore, we halt immediately
5524                                // with an agent error under these circumstances
5525                                if (mOnlyPackage != null) {
5526                                    setResult(RestoreEngine.TARGET_FAILURE);
5527                                    setRunning(false);
5528                                    return false;
5529                                }
5530                            }
5531                        }
5532
5533                        // Problems setting up the agent communication, an explicitly
5534                        // dropped file, or an already-ignored package: skip to the
5535                        // next stream entry by reading and discarding this file.
5536                        if (!okay) {
5537                            if (MORE_DEBUG) Slog.d(TAG, "[discarding file content]");
5538                            long bytesToConsume = (info.size + 511) & ~511;
5539                            while (bytesToConsume > 0) {
5540                                int toRead = (bytesToConsume > mBuffer.length)
5541                                        ? mBuffer.length : (int)bytesToConsume;
5542                                long nRead = instream.read(mBuffer, 0, toRead);
5543                                if (nRead >= 0) mBytes += nRead;
5544                                if (nRead <= 0) break;
5545                                bytesToConsume -= nRead;
5546                            }
5547                        }
5548                    }
5549                }
5550            } catch (IOException e) {
5551                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
5552                setResult(RestoreEngine.TRANSPORT_FAILURE);
5553                info = null;
5554            }
5555
5556            // If we got here we're either running smoothly or we've finished
5557            if (info == null) {
5558                if (MORE_DEBUG) {
5559                    Slog.i(TAG, "No [more] data for this package; tearing down");
5560                }
5561                tearDownPipes();
5562                setRunning(false);
5563                if (mustKillAgent) {
5564                    tearDownAgent(mTargetApp);
5565                }
5566            }
5567            return (info != null);
5568        }
5569
5570        void setUpPipes() throws IOException {
5571            mPipes = ParcelFileDescriptor.createPipe();
5572        }
5573
5574        void tearDownPipes() {
5575            if (mPipes != null) {
5576                try {
5577                    mPipes[0].close();
5578                    mPipes[0] = null;
5579                    mPipes[1].close();
5580                    mPipes[1] = null;
5581                } catch (IOException e) {
5582                    Slog.w(TAG, "Couldn't close agent pipes", e);
5583                }
5584                mPipes = null;
5585            }
5586        }
5587
5588        void tearDownAgent(ApplicationInfo app) {
5589            if (mAgent != null) {
5590                tearDownAgentAndKill(app);
5591                mAgent = null;
5592            }
5593        }
5594
5595        class RestoreInstallObserver extends PackageInstallObserver {
5596            final AtomicBoolean mDone = new AtomicBoolean();
5597            String mPackageName;
5598            int mResult;
5599
5600            public void reset() {
5601                synchronized (mDone) {
5602                    mDone.set(false);
5603                }
5604            }
5605
5606            public void waitForCompletion() {
5607                synchronized (mDone) {
5608                    while (mDone.get() == false) {
5609                        try {
5610                            mDone.wait();
5611                        } catch (InterruptedException e) { }
5612                    }
5613                }
5614            }
5615
5616            int getResult() {
5617                return mResult;
5618            }
5619
5620            @Override
5621            public void onPackageInstalled(String packageName, int returnCode,
5622                    String msg, Bundle extras) {
5623                synchronized (mDone) {
5624                    mResult = returnCode;
5625                    mPackageName = packageName;
5626                    mDone.set(true);
5627                    mDone.notifyAll();
5628                }
5629            }
5630        }
5631
5632        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
5633            final AtomicBoolean mDone = new AtomicBoolean();
5634            int mResult;
5635
5636            public void reset() {
5637                synchronized (mDone) {
5638                    mDone.set(false);
5639                }
5640            }
5641
5642            public void waitForCompletion() {
5643                synchronized (mDone) {
5644                    while (mDone.get() == false) {
5645                        try {
5646                            mDone.wait();
5647                        } catch (InterruptedException e) { }
5648                    }
5649                }
5650            }
5651
5652            @Override
5653            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
5654                synchronized (mDone) {
5655                    mResult = returnCode;
5656                    mDone.set(true);
5657                    mDone.notifyAll();
5658                }
5659            }
5660        }
5661
5662        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
5663        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
5664
5665        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
5666            boolean okay = true;
5667
5668            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
5669
5670            // The file content is an .apk file.  Copy it out to a staging location and
5671            // attempt to install it.
5672            File apkFile = new File(mDataDir, info.packageName);
5673            try {
5674                FileOutputStream apkStream = new FileOutputStream(apkFile);
5675                byte[] buffer = new byte[32 * 1024];
5676                long size = info.size;
5677                while (size > 0) {
5678                    long toRead = (buffer.length < size) ? buffer.length : size;
5679                    int didRead = instream.read(buffer, 0, (int)toRead);
5680                    if (didRead >= 0) mBytes += didRead;
5681                    apkStream.write(buffer, 0, didRead);
5682                    size -= didRead;
5683                }
5684                apkStream.close();
5685
5686                // make sure the installer can read it
5687                apkFile.setReadable(true, false);
5688
5689                // Now install it
5690                Uri packageUri = Uri.fromFile(apkFile);
5691                mInstallObserver.reset();
5692                mPackageManager.installPackage(packageUri, mInstallObserver,
5693                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
5694                        installerPackage);
5695                mInstallObserver.waitForCompletion();
5696
5697                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
5698                    // The only time we continue to accept install of data even if the
5699                    // apk install failed is if we had already determined that we could
5700                    // accept the data regardless.
5701                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
5702                        okay = false;
5703                    }
5704                } else {
5705                    // Okay, the install succeeded.  Make sure it was the right app.
5706                    boolean uninstall = false;
5707                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
5708                        Slog.w(TAG, "Restore stream claimed to include apk for "
5709                                + info.packageName + " but apk was really "
5710                                + mInstallObserver.mPackageName);
5711                        // delete the package we just put in place; it might be fraudulent
5712                        okay = false;
5713                        uninstall = true;
5714                    } else {
5715                        try {
5716                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
5717                                    PackageManager.GET_SIGNATURES);
5718                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
5719                                Slog.w(TAG, "Restore stream contains apk of package "
5720                                        + info.packageName + " but it disallows backup/restore");
5721                                okay = false;
5722                            } else {
5723                                // So far so good -- do the signatures match the manifest?
5724                                Signature[] sigs = mManifestSignatures.get(info.packageName);
5725                                if (signaturesMatch(sigs, pkg)) {
5726                                    // If this is a system-uid app without a declared backup agent,
5727                                    // don't restore any of the file data.
5728                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
5729                                            && (pkg.applicationInfo.backupAgentName == null)) {
5730                                        Slog.w(TAG, "Installed app " + info.packageName
5731                                                + " has restricted uid and no agent");
5732                                        okay = false;
5733                                    }
5734                                } else {
5735                                    Slog.w(TAG, "Installed app " + info.packageName
5736                                            + " signatures do not match restore manifest");
5737                                    okay = false;
5738                                    uninstall = true;
5739                                }
5740                            }
5741                        } catch (NameNotFoundException e) {
5742                            Slog.w(TAG, "Install of package " + info.packageName
5743                                    + " succeeded but now not found");
5744                            okay = false;
5745                        }
5746                    }
5747
5748                    // If we're not okay at this point, we need to delete the package
5749                    // that we just installed.
5750                    if (uninstall) {
5751                        mDeleteObserver.reset();
5752                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
5753                                mDeleteObserver, 0);
5754                        mDeleteObserver.waitForCompletion();
5755                    }
5756                }
5757            } catch (IOException e) {
5758                Slog.e(TAG, "Unable to transcribe restored apk for install");
5759                okay = false;
5760            } finally {
5761                apkFile.delete();
5762            }
5763
5764            return okay;
5765        }
5766
5767        // Given an actual file content size, consume the post-content padding mandated
5768        // by the tar format.
5769        void skipTarPadding(long size, InputStream instream) throws IOException {
5770            long partial = (size + 512) % 512;
5771            if (partial > 0) {
5772                final int needed = 512 - (int)partial;
5773                if (MORE_DEBUG) {
5774                    Slog.i(TAG, "Skipping tar padding: " + needed + " bytes");
5775                }
5776                byte[] buffer = new byte[needed];
5777                if (readExactly(instream, buffer, 0, needed) == needed) {
5778                    mBytes += needed;
5779                } else throw new IOException("Unexpected EOF in padding");
5780            }
5781        }
5782
5783        // Read a widget metadata file, returning the restored blob
5784        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
5785            // Fail on suspiciously large widget dump files
5786            if (info.size > 64 * 1024) {
5787                throw new IOException("Metadata too big; corrupt? size=" + info.size);
5788            }
5789
5790            byte[] buffer = new byte[(int) info.size];
5791            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5792                mBytes += info.size;
5793            } else throw new IOException("Unexpected EOF in widget data");
5794
5795            String[] str = new String[1];
5796            int offset = extractLine(buffer, 0, str);
5797            int version = Integer.parseInt(str[0]);
5798            if (version == BACKUP_MANIFEST_VERSION) {
5799                offset = extractLine(buffer, offset, str);
5800                final String pkg = str[0];
5801                if (info.packageName.equals(pkg)) {
5802                    // Data checks out -- the rest of the buffer is a concatenation of
5803                    // binary blobs as described in the comment at writeAppWidgetData()
5804                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
5805                            offset, buffer.length - offset);
5806                    DataInputStream in = new DataInputStream(bin);
5807                    while (bin.available() > 0) {
5808                        int token = in.readInt();
5809                        int size = in.readInt();
5810                        if (size > 64 * 1024) {
5811                            throw new IOException("Datum "
5812                                    + Integer.toHexString(token)
5813                                    + " too big; corrupt? size=" + info.size);
5814                        }
5815                        switch (token) {
5816                            case BACKUP_WIDGET_METADATA_TOKEN:
5817                            {
5818                                if (MORE_DEBUG) {
5819                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
5820                                }
5821                                mWidgetData = new byte[size];
5822                                in.read(mWidgetData);
5823                                break;
5824                            }
5825                            default:
5826                            {
5827                                if (DEBUG) {
5828                                    Slog.i(TAG, "Ignoring metadata blob "
5829                                            + Integer.toHexString(token)
5830                                            + " for " + info.packageName);
5831                                }
5832                                in.skipBytes(size);
5833                                break;
5834                            }
5835                        }
5836                    }
5837                } else {
5838                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
5839                            + " but widget data for " + pkg);
5840                }
5841            } else {
5842                Slog.w(TAG, "Unsupported metadata version " + version);
5843            }
5844        }
5845
5846        // Returns a policy constant
5847        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
5848                throws IOException {
5849            // Fail on suspiciously large manifest files
5850            if (info.size > 64 * 1024) {
5851                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
5852            }
5853
5854            byte[] buffer = new byte[(int) info.size];
5855            if (MORE_DEBUG) {
5856                Slog.i(TAG, "   readAppManifest() looking for " + info.size + " bytes, "
5857                        + mBytes + " already consumed");
5858            }
5859            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5860                mBytes += info.size;
5861            } else throw new IOException("Unexpected EOF in manifest");
5862
5863            RestorePolicy policy = RestorePolicy.IGNORE;
5864            String[] str = new String[1];
5865            int offset = 0;
5866
5867            try {
5868                offset = extractLine(buffer, offset, str);
5869                int version = Integer.parseInt(str[0]);
5870                if (version == BACKUP_MANIFEST_VERSION) {
5871                    offset = extractLine(buffer, offset, str);
5872                    String manifestPackage = str[0];
5873                    // TODO: handle <original-package>
5874                    if (manifestPackage.equals(info.packageName)) {
5875                        offset = extractLine(buffer, offset, str);
5876                        version = Integer.parseInt(str[0]);  // app version
5877                        offset = extractLine(buffer, offset, str);
5878                        // This is the platform version, which we don't use, but we parse it
5879                        // as a safety against corruption in the manifest.
5880                        Integer.parseInt(str[0]);
5881                        offset = extractLine(buffer, offset, str);
5882                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
5883                        offset = extractLine(buffer, offset, str);
5884                        boolean hasApk = str[0].equals("1");
5885                        offset = extractLine(buffer, offset, str);
5886                        int numSigs = Integer.parseInt(str[0]);
5887                        if (numSigs > 0) {
5888                            Signature[] sigs = new Signature[numSigs];
5889                            for (int i = 0; i < numSigs; i++) {
5890                                offset = extractLine(buffer, offset, str);
5891                                sigs[i] = new Signature(str[0]);
5892                            }
5893                            mManifestSignatures.put(info.packageName, sigs);
5894
5895                            // Okay, got the manifest info we need...
5896                            try {
5897                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
5898                                        info.packageName, PackageManager.GET_SIGNATURES);
5899                                // Fall through to IGNORE if the app explicitly disallows backup
5900                                final int flags = pkgInfo.applicationInfo.flags;
5901                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
5902                                    // Restore system-uid-space packages only if they have
5903                                    // defined a custom backup agent
5904                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
5905                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
5906                                        // Verify signatures against any installed version; if they
5907                                        // don't match, then we fall though and ignore the data.  The
5908                                        // signatureMatch() method explicitly ignores the signature
5909                                        // check for packages installed on the system partition, because
5910                                        // such packages are signed with the platform cert instead of
5911                                        // the app developer's cert, so they're different on every
5912                                        // device.
5913                                        if (signaturesMatch(sigs, pkgInfo)) {
5914                                            if (pkgInfo.versionCode >= version) {
5915                                                Slog.i(TAG, "Sig + version match; taking data");
5916                                                policy = RestorePolicy.ACCEPT;
5917                                            } else {
5918                                                // The data is from a newer version of the app than
5919                                                // is presently installed.  That means we can only
5920                                                // use it if the matching apk is also supplied.
5921                                                if (mAllowApks) {
5922                                                    Slog.i(TAG, "Data version " + version
5923                                                            + " is newer than installed version "
5924                                                            + pkgInfo.versionCode
5925                                                            + " - requiring apk");
5926                                                    policy = RestorePolicy.ACCEPT_IF_APK;
5927                                                } else {
5928                                                    Slog.i(TAG, "Data requires newer version "
5929                                                            + version + "; ignoring");
5930                                                    policy = RestorePolicy.IGNORE;
5931                                                }
5932                                            }
5933                                        } else {
5934                                            Slog.w(TAG, "Restore manifest signatures do not match "
5935                                                    + "installed application for " + info.packageName);
5936                                        }
5937                                    } else {
5938                                        Slog.w(TAG, "Package " + info.packageName
5939                                                + " is system level with no agent");
5940                                    }
5941                                } else {
5942                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
5943                                            + info.packageName + " but allowBackup=false");
5944                                }
5945                            } catch (NameNotFoundException e) {
5946                                // Okay, the target app isn't installed.  We can process
5947                                // the restore properly only if the dataset provides the
5948                                // apk file and we can successfully install it.
5949                                if (mAllowApks) {
5950                                    if (DEBUG) Slog.i(TAG, "Package " + info.packageName
5951                                            + " not installed; requiring apk in dataset");
5952                                    policy = RestorePolicy.ACCEPT_IF_APK;
5953                                } else {
5954                                    policy = RestorePolicy.IGNORE;
5955                                }
5956                            }
5957
5958                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
5959                                Slog.i(TAG, "Cannot restore package " + info.packageName
5960                                        + " without the matching .apk");
5961                            }
5962                        } else {
5963                            Slog.i(TAG, "Missing signature on backed-up package "
5964                                    + info.packageName);
5965                        }
5966                    } else {
5967                        Slog.i(TAG, "Expected package " + info.packageName
5968                                + " but restore manifest claims " + manifestPackage);
5969                    }
5970                } else {
5971                    Slog.i(TAG, "Unknown restore manifest version " + version
5972                            + " for package " + info.packageName);
5973                }
5974            } catch (NumberFormatException e) {
5975                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
5976            } catch (IllegalArgumentException e) {
5977                Slog.w(TAG, e.getMessage());
5978            }
5979
5980            return policy;
5981        }
5982
5983        // Builds a line from a byte buffer starting at 'offset', and returns
5984        // the index of the next unconsumed data in the buffer.
5985        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
5986            final int end = buffer.length;
5987            if (offset >= end) throw new IOException("Incomplete data");
5988
5989            int pos;
5990            for (pos = offset; pos < end; pos++) {
5991                byte c = buffer[pos];
5992                // at LF we declare end of line, and return the next char as the
5993                // starting point for the next time through
5994                if (c == '\n') {
5995                    break;
5996                }
5997            }
5998            outStr[0] = new String(buffer, offset, pos - offset);
5999            pos++;  // may be pointing an extra byte past the end but that's okay
6000            return pos;
6001        }
6002
6003        void dumpFileMetadata(FileMetadata info) {
6004            if (MORE_DEBUG) {
6005                StringBuilder b = new StringBuilder(128);
6006
6007                // mode string
6008                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
6009                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
6010                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
6011                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
6012                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
6013                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
6014                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
6015                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
6016                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
6017                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
6018                b.append(String.format(" %9d ", info.size));
6019
6020                Date stamp = new Date(info.mtime);
6021                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
6022
6023                b.append(info.packageName);
6024                b.append(" :: ");
6025                b.append(info.domain);
6026                b.append(" :: ");
6027                b.append(info.path);
6028
6029                Slog.i(TAG, b.toString());
6030            }
6031        }
6032
6033        // Consume a tar file header block [sequence] and accumulate the relevant metadata
6034        FileMetadata readTarHeaders(InputStream instream) throws IOException {
6035            byte[] block = new byte[512];
6036            FileMetadata info = null;
6037
6038            boolean gotHeader = readTarHeader(instream, block);
6039            if (gotHeader) {
6040                try {
6041                    // okay, presume we're okay, and extract the various metadata
6042                    info = new FileMetadata();
6043                    info.size = extractRadix(block, 124, 12, 8);
6044                    info.mtime = extractRadix(block, 136, 12, 8);
6045                    info.mode = extractRadix(block, 100, 8, 8);
6046
6047                    info.path = extractString(block, 345, 155); // prefix
6048                    String path = extractString(block, 0, 100);
6049                    if (path.length() > 0) {
6050                        if (info.path.length() > 0) info.path += '/';
6051                        info.path += path;
6052                    }
6053
6054                    // tar link indicator field: 1 byte at offset 156 in the header.
6055                    int typeChar = block[156];
6056                    if (typeChar == 'x') {
6057                        // pax extended header, so we need to read that
6058                        gotHeader = readPaxExtendedHeader(instream, info);
6059                        if (gotHeader) {
6060                            // and after a pax extended header comes another real header -- read
6061                            // that to find the real file type
6062                            gotHeader = readTarHeader(instream, block);
6063                        }
6064                        if (!gotHeader) throw new IOException("Bad or missing pax header");
6065
6066                        typeChar = block[156];
6067                    }
6068
6069                    switch (typeChar) {
6070                        case '0': info.type = BackupAgent.TYPE_FILE; break;
6071                        case '5': {
6072                            info.type = BackupAgent.TYPE_DIRECTORY;
6073                            if (info.size != 0) {
6074                                Slog.w(TAG, "Directory entry with nonzero size in header");
6075                                info.size = 0;
6076                            }
6077                            break;
6078                        }
6079                        case 0: {
6080                            // presume EOF
6081                            if (MORE_DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
6082                            return null;
6083                        }
6084                        default: {
6085                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
6086                            throw new IOException("Unknown entity type " + typeChar);
6087                        }
6088                    }
6089
6090                    // Parse out the path
6091                    //
6092                    // first: apps/shared/unrecognized
6093                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
6094                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
6095                        // File in shared storage.  !!! TODO: implement this.
6096                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
6097                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
6098                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
6099                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
6100                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
6101                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
6102                        // App content!  Parse out the package name and domain
6103
6104                        // strip the apps/ prefix
6105                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
6106
6107                        // extract the package name
6108                        int slash = info.path.indexOf('/');
6109                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
6110                        info.packageName = info.path.substring(0, slash);
6111                        info.path = info.path.substring(slash+1);
6112
6113                        // if it's a manifest or metadata payload we're done, otherwise parse
6114                        // out the domain into which the file will be restored
6115                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
6116                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
6117                            slash = info.path.indexOf('/');
6118                            if (slash < 0) {
6119                                throw new IOException("Illegal semantic path in non-manifest "
6120                                        + info.path);
6121                            }
6122                            info.domain = info.path.substring(0, slash);
6123                            info.path = info.path.substring(slash + 1);
6124                        }
6125                    }
6126                } catch (IOException e) {
6127                    if (DEBUG) {
6128                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
6129                        if (MORE_DEBUG) {
6130                            HEXLOG(block);
6131                        }
6132                    }
6133                    throw e;
6134                }
6135            }
6136            return info;
6137        }
6138
6139        private boolean isRestorableFile(FileMetadata info) {
6140            if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
6141                if (MORE_DEBUG) {
6142                    Slog.i(TAG, "Dropping cache file path " + info.path);
6143                }
6144                return false;
6145            }
6146
6147            if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
6148                // It's possible this is "no-backup" dir contents in an archive stream
6149                // produced on a device running a version of the OS that predates that
6150                // API.  Respect the no-backup intention and don't let the data get to
6151                // the app.
6152                if (info.path.startsWith("no_backup/")) {
6153                    if (MORE_DEBUG) {
6154                        Slog.i(TAG, "Dropping no_backup file path " + info.path);
6155                    }
6156                    return false;
6157                }
6158            }
6159
6160            // The path needs to be canonical
6161            if (info.path.contains("..") || info.path.contains("//")) {
6162                if (MORE_DEBUG) {
6163                    Slog.w(TAG, "Dropping invalid path " + info.path);
6164                }
6165                return false;
6166            }
6167
6168            // Otherwise we think this file is good to go
6169            return true;
6170        }
6171
6172        private void HEXLOG(byte[] block) {
6173            int offset = 0;
6174            int todo = block.length;
6175            StringBuilder buf = new StringBuilder(64);
6176            while (todo > 0) {
6177                buf.append(String.format("%04x   ", offset));
6178                int numThisLine = (todo > 16) ? 16 : todo;
6179                for (int i = 0; i < numThisLine; i++) {
6180                    buf.append(String.format("%02x ", block[offset+i]));
6181                }
6182                Slog.i("hexdump", buf.toString());
6183                buf.setLength(0);
6184                todo -= numThisLine;
6185                offset += numThisLine;
6186            }
6187        }
6188
6189        // Read exactly the given number of bytes into a buffer at the stated offset.
6190        // Returns false if EOF is encountered before the requested number of bytes
6191        // could be read.
6192        int readExactly(InputStream in, byte[] buffer, int offset, int size)
6193                throws IOException {
6194            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
6195if (MORE_DEBUG) Slog.i(TAG, "  ... readExactly(" + size + ") called");
6196            int soFar = 0;
6197            while (soFar < size) {
6198                int nRead = in.read(buffer, offset + soFar, size - soFar);
6199                if (nRead <= 0) {
6200                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
6201                    break;
6202                }
6203                soFar += nRead;
6204if (MORE_DEBUG) Slog.v(TAG, "   + got " + nRead + "; now wanting " + (size - soFar));
6205            }
6206            return soFar;
6207        }
6208
6209        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
6210            final int got = readExactly(instream, block, 0, 512);
6211            if (got == 0) return false;     // Clean EOF
6212            if (got < 512) throw new IOException("Unable to read full block header");
6213            mBytes += 512;
6214            return true;
6215        }
6216
6217        // overwrites 'info' fields based on the pax extended header
6218        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
6219                throws IOException {
6220            // We should never see a pax extended header larger than this
6221            if (info.size > 32*1024) {
6222                Slog.w(TAG, "Suspiciously large pax header size " + info.size
6223                        + " - aborting");
6224                throw new IOException("Sanity failure: pax header size " + info.size);
6225            }
6226
6227            // read whole blocks, not just the content size
6228            int numBlocks = (int)((info.size + 511) >> 9);
6229            byte[] data = new byte[numBlocks * 512];
6230            if (readExactly(instream, data, 0, data.length) < data.length) {
6231                throw new IOException("Unable to read full pax header");
6232            }
6233            mBytes += data.length;
6234
6235            final int contentSize = (int) info.size;
6236            int offset = 0;
6237            do {
6238                // extract the line at 'offset'
6239                int eol = offset+1;
6240                while (eol < contentSize && data[eol] != ' ') eol++;
6241                if (eol >= contentSize) {
6242                    // error: we just hit EOD looking for the end of the size field
6243                    throw new IOException("Invalid pax data");
6244                }
6245                // eol points to the space between the count and the key
6246                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
6247                int key = eol + 1;  // start of key=value
6248                eol = offset + linelen - 1; // trailing LF
6249                int value;
6250                for (value = key+1; data[value] != '=' && value <= eol; value++);
6251                if (value > eol) {
6252                    throw new IOException("Invalid pax declaration");
6253                }
6254
6255                // pax requires that key/value strings be in UTF-8
6256                String keyStr = new String(data, key, value-key, "UTF-8");
6257                // -1 to strip the trailing LF
6258                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
6259
6260                if ("path".equals(keyStr)) {
6261                    info.path = valStr;
6262                } else if ("size".equals(keyStr)) {
6263                    info.size = Long.parseLong(valStr);
6264                } else {
6265                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
6266                }
6267
6268                offset += linelen;
6269            } while (offset < contentSize);
6270
6271            return true;
6272        }
6273
6274        long extractRadix(byte[] data, int offset, int maxChars, int radix)
6275                throws IOException {
6276            long value = 0;
6277            final int end = offset + maxChars;
6278            for (int i = offset; i < end; i++) {
6279                final byte b = data[i];
6280                // Numeric fields in tar can terminate with either NUL or SPC
6281                if (b == 0 || b == ' ') break;
6282                if (b < '0' || b > ('0' + radix - 1)) {
6283                    throw new IOException("Invalid number in header: '" + (char)b
6284                            + "' for radix " + radix);
6285                }
6286                value = radix * value + (b - '0');
6287            }
6288            return value;
6289        }
6290
6291        String extractString(byte[] data, int offset, int maxChars) throws IOException {
6292            final int end = offset + maxChars;
6293            int eos = offset;
6294            // tar string fields terminate early with a NUL
6295            while (eos < end && data[eos] != 0) eos++;
6296            return new String(data, offset, eos-offset, "US-ASCII");
6297        }
6298
6299        void sendStartRestore() {
6300            if (mObserver != null) {
6301                try {
6302                    mObserver.onStartRestore();
6303                } catch (RemoteException e) {
6304                    Slog.w(TAG, "full restore observer went away: startRestore");
6305                    mObserver = null;
6306                }
6307            }
6308        }
6309
6310        void sendOnRestorePackage(String name) {
6311            if (mObserver != null) {
6312                try {
6313                    // TODO: use a more user-friendly name string
6314                    mObserver.onRestorePackage(name);
6315                } catch (RemoteException e) {
6316                    Slog.w(TAG, "full restore observer went away: restorePackage");
6317                    mObserver = null;
6318                }
6319            }
6320        }
6321
6322        void sendEndRestore() {
6323            if (mObserver != null) {
6324                try {
6325                    mObserver.onEndRestore();
6326                } catch (RemoteException e) {
6327                    Slog.w(TAG, "full restore observer went away: endRestore");
6328                    mObserver = null;
6329                }
6330            }
6331        }
6332    }
6333
6334    // ***** end new engine class ***
6335
6336    class PerformAdbRestoreTask implements Runnable {
6337        ParcelFileDescriptor mInputFile;
6338        String mCurrentPassword;
6339        String mDecryptPassword;
6340        IFullBackupRestoreObserver mObserver;
6341        AtomicBoolean mLatchObject;
6342        IBackupAgent mAgent;
6343        String mAgentPackage;
6344        ApplicationInfo mTargetApp;
6345        FullBackupObbConnection mObbConnection = null;
6346        ParcelFileDescriptor[] mPipes = null;
6347        byte[] mWidgetData = null;
6348
6349        long mBytes;
6350
6351        // possible handling states for a given package in the restore dataset
6352        final HashMap<String, RestorePolicy> mPackagePolicies
6353                = new HashMap<String, RestorePolicy>();
6354
6355        // installer package names for each encountered app, derived from the manifests
6356        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
6357
6358        // Signatures for a given package found in its manifest file
6359        final HashMap<String, Signature[]> mManifestSignatures
6360                = new HashMap<String, Signature[]>();
6361
6362        // Packages we've already wiped data on when restoring their first file
6363        final HashSet<String> mClearedPackages = new HashSet<String>();
6364
6365        PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
6366                IFullBackupRestoreObserver observer, AtomicBoolean latch) {
6367            mInputFile = fd;
6368            mCurrentPassword = curPassword;
6369            mDecryptPassword = decryptPassword;
6370            mObserver = observer;
6371            mLatchObject = latch;
6372            mAgent = null;
6373            mAgentPackage = null;
6374            mTargetApp = null;
6375            mObbConnection = new FullBackupObbConnection();
6376
6377            // Which packages we've already wiped data on.  We prepopulate this
6378            // with a whitelist of packages known to be unclearable.
6379            mClearedPackages.add("android");
6380            mClearedPackages.add(SETTINGS_PACKAGE);
6381        }
6382
6383        class RestoreFileRunnable implements Runnable {
6384            IBackupAgent mAgent;
6385            FileMetadata mInfo;
6386            ParcelFileDescriptor mSocket;
6387            int mToken;
6388
6389            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
6390                    ParcelFileDescriptor socket, int token) throws IOException {
6391                mAgent = agent;
6392                mInfo = info;
6393                mToken = token;
6394
6395                // This class is used strictly for process-local binder invocations.  The
6396                // semantics of ParcelFileDescriptor differ in this case; in particular, we
6397                // do not automatically get a 'dup'ed descriptor that we can can continue
6398                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
6399                // before proceeding to do the restore.
6400                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
6401            }
6402
6403            @Override
6404            public void run() {
6405                try {
6406                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
6407                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
6408                            mToken, mBackupManagerBinder);
6409                } catch (RemoteException e) {
6410                    // never happens; this is used strictly for local binder calls
6411                }
6412            }
6413        }
6414
6415        @Override
6416        public void run() {
6417            Slog.i(TAG, "--- Performing full-dataset restore ---");
6418            mObbConnection.establish();
6419            sendStartRestore();
6420
6421            // Are we able to restore shared-storage data?
6422            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
6423                mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
6424            }
6425
6426            FileInputStream rawInStream = null;
6427            DataInputStream rawDataIn = null;
6428            try {
6429                if (!backupPasswordMatches(mCurrentPassword)) {
6430                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
6431                    return;
6432                }
6433
6434                mBytes = 0;
6435                byte[] buffer = new byte[32 * 1024];
6436                rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
6437                rawDataIn = new DataInputStream(rawInStream);
6438
6439                // First, parse out the unencrypted/uncompressed header
6440                boolean compressed = false;
6441                InputStream preCompressStream = rawInStream;
6442                final InputStream in;
6443
6444                boolean okay = false;
6445                final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
6446                byte[] streamHeader = new byte[headerLen];
6447                rawDataIn.readFully(streamHeader);
6448                byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
6449                if (Arrays.equals(magicBytes, streamHeader)) {
6450                    // okay, header looks good.  now parse out the rest of the fields.
6451                    String s = readHeaderLine(rawInStream);
6452                    final int archiveVersion = Integer.parseInt(s);
6453                    if (archiveVersion <= BACKUP_FILE_VERSION) {
6454                        // okay, it's a version we recognize.  if it's version 1, we may need
6455                        // to try two different PBKDF2 regimes to compare checksums.
6456                        final boolean pbkdf2Fallback = (archiveVersion == 1);
6457
6458                        s = readHeaderLine(rawInStream);
6459                        compressed = (Integer.parseInt(s) != 0);
6460                        s = readHeaderLine(rawInStream);
6461                        if (s.equals("none")) {
6462                            // no more header to parse; we're good to go
6463                            okay = true;
6464                        } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
6465                            preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
6466                                    rawInStream);
6467                            if (preCompressStream != null) {
6468                                okay = true;
6469                            }
6470                        } else Slog.w(TAG, "Archive is encrypted but no password given");
6471                    } else Slog.w(TAG, "Wrong header version: " + s);
6472                } else Slog.w(TAG, "Didn't read the right header magic");
6473
6474                if (!okay) {
6475                    Slog.w(TAG, "Invalid restore data; aborting.");
6476                    return;
6477                }
6478
6479                // okay, use the right stream layer based on compression
6480                in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
6481
6482                boolean didRestore;
6483                do {
6484                    didRestore = restoreOneFile(in, buffer);
6485                } while (didRestore);
6486
6487                if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
6488            } catch (IOException e) {
6489                Slog.e(TAG, "Unable to read restore input");
6490            } finally {
6491                tearDownPipes();
6492                tearDownAgent(mTargetApp);
6493
6494                try {
6495                    if (rawDataIn != null) rawDataIn.close();
6496                    if (rawInStream != null) rawInStream.close();
6497                    mInputFile.close();
6498                } catch (IOException e) {
6499                    Slog.w(TAG, "Close of restore data pipe threw", e);
6500                    /* nothing we can do about this */
6501                }
6502                synchronized (mCurrentOpLock) {
6503                    mCurrentOperations.clear();
6504                }
6505                synchronized (mLatchObject) {
6506                    mLatchObject.set(true);
6507                    mLatchObject.notifyAll();
6508                }
6509                mObbConnection.tearDown();
6510                sendEndRestore();
6511                Slog.d(TAG, "Full restore pass complete.");
6512                mWakelock.release();
6513            }
6514        }
6515
6516        String readHeaderLine(InputStream in) throws IOException {
6517            int c;
6518            StringBuilder buffer = new StringBuilder(80);
6519            while ((c = in.read()) >= 0) {
6520                if (c == '\n') break;   // consume and discard the newlines
6521                buffer.append((char)c);
6522            }
6523            return buffer.toString();
6524        }
6525
6526        InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
6527                int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
6528                boolean doLog) {
6529            InputStream result = null;
6530
6531            try {
6532                Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
6533                SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt,
6534                        rounds);
6535                byte[] IV = hexToByteArray(userIvHex);
6536                IvParameterSpec ivSpec = new IvParameterSpec(IV);
6537                c.init(Cipher.DECRYPT_MODE,
6538                        new SecretKeySpec(userKey.getEncoded(), "AES"),
6539                        ivSpec);
6540                byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
6541                byte[] mkBlob = c.doFinal(mkCipher);
6542
6543                // first, the master key IV
6544                int offset = 0;
6545                int len = mkBlob[offset++];
6546                IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
6547                offset += len;
6548                // then the master key itself
6549                len = mkBlob[offset++];
6550                byte[] mk = Arrays.copyOfRange(mkBlob,
6551                        offset, offset + len);
6552                offset += len;
6553                // and finally the master key checksum hash
6554                len = mkBlob[offset++];
6555                byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
6556                        offset, offset + len);
6557
6558                // now validate the decrypted master key against the checksum
6559                byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds);
6560                if (Arrays.equals(calculatedCk, mkChecksum)) {
6561                    ivSpec = new IvParameterSpec(IV);
6562                    c.init(Cipher.DECRYPT_MODE,
6563                            new SecretKeySpec(mk, "AES"),
6564                            ivSpec);
6565                    // Only if all of the above worked properly will 'result' be assigned
6566                    result = new CipherInputStream(rawInStream, c);
6567                } else if (doLog) Slog.w(TAG, "Incorrect password");
6568            } catch (InvalidAlgorithmParameterException e) {
6569                if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e);
6570            } catch (BadPaddingException e) {
6571                // This case frequently occurs when the wrong password is used to decrypt
6572                // the master key.  Use the identical "incorrect password" log text as is
6573                // used in the checksum failure log in order to avoid providing additional
6574                // information to an attacker.
6575                if (doLog) Slog.w(TAG, "Incorrect password");
6576            } catch (IllegalBlockSizeException e) {
6577                if (doLog) Slog.w(TAG, "Invalid block size in master key");
6578            } catch (NoSuchAlgorithmException e) {
6579                if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!");
6580            } catch (NoSuchPaddingException e) {
6581                if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!");
6582            } catch (InvalidKeyException e) {
6583                if (doLog) Slog.w(TAG, "Illegal password; aborting");
6584            }
6585
6586            return result;
6587        }
6588
6589        InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
6590                InputStream rawInStream) {
6591            InputStream result = null;
6592            try {
6593                if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
6594
6595                    String userSaltHex = readHeaderLine(rawInStream); // 5
6596                    byte[] userSalt = hexToByteArray(userSaltHex);
6597
6598                    String ckSaltHex = readHeaderLine(rawInStream); // 6
6599                    byte[] ckSalt = hexToByteArray(ckSaltHex);
6600
6601                    int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
6602                    String userIvHex = readHeaderLine(rawInStream); // 8
6603
6604                    String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
6605
6606                    // decrypt the master key blob
6607                    result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt,
6608                            rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
6609                    if (result == null && pbkdf2Fallback) {
6610                        result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt,
6611                                rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
6612                    }
6613                } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
6614            } catch (NumberFormatException e) {
6615                Slog.w(TAG, "Can't parse restore data header");
6616            } catch (IOException e) {
6617                Slog.w(TAG, "Can't read input header");
6618            }
6619
6620            return result;
6621        }
6622
6623        boolean restoreOneFile(InputStream instream, byte[] buffer) {
6624            FileMetadata info;
6625            try {
6626                info = readTarHeaders(instream);
6627                if (info != null) {
6628                    if (MORE_DEBUG) {
6629                        dumpFileMetadata(info);
6630                    }
6631
6632                    final String pkg = info.packageName;
6633                    if (!pkg.equals(mAgentPackage)) {
6634                        // okay, change in package; set up our various
6635                        // bookkeeping if we haven't seen it yet
6636                        if (!mPackagePolicies.containsKey(pkg)) {
6637                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6638                        }
6639
6640                        // Clean up the previous agent relationship if necessary,
6641                        // and let the observer know we're considering a new app.
6642                        if (mAgent != null) {
6643                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
6644                            // Now we're really done
6645                            tearDownPipes();
6646                            tearDownAgent(mTargetApp);
6647                            mTargetApp = null;
6648                            mAgentPackage = null;
6649                        }
6650                    }
6651
6652                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
6653                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
6654                        mPackageInstallers.put(pkg, info.installerPackageName);
6655                        // We've read only the manifest content itself at this point,
6656                        // so consume the footer before looping around to the next
6657                        // input file
6658                        skipTarPadding(info.size, instream);
6659                        sendOnRestorePackage(pkg);
6660                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
6661                        // Metadata blobs!
6662                        readMetadata(info, instream);
6663                        skipTarPadding(info.size, instream);
6664                    } else {
6665                        // Non-manifest, so it's actual file data.  Is this a package
6666                        // we're ignoring?
6667                        boolean okay = true;
6668                        RestorePolicy policy = mPackagePolicies.get(pkg);
6669                        switch (policy) {
6670                            case IGNORE:
6671                                okay = false;
6672                                break;
6673
6674                            case ACCEPT_IF_APK:
6675                                // If we're in accept-if-apk state, then the first file we
6676                                // see MUST be the apk.
6677                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6678                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
6679                                    // Try to install the app.
6680                                    String installerName = mPackageInstallers.get(pkg);
6681                                    okay = installApk(info, installerName, instream);
6682                                    // good to go; promote to ACCEPT
6683                                    mPackagePolicies.put(pkg, (okay)
6684                                            ? RestorePolicy.ACCEPT
6685                                            : RestorePolicy.IGNORE);
6686                                    // At this point we've consumed this file entry
6687                                    // ourselves, so just strip the tar footer and
6688                                    // go on to the next file in the input stream
6689                                    skipTarPadding(info.size, instream);
6690                                    return true;
6691                                } else {
6692                                    // File data before (or without) the apk.  We can't
6693                                    // handle it coherently in this case so ignore it.
6694                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6695                                    okay = false;
6696                                }
6697                                break;
6698
6699                            case ACCEPT:
6700                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6701                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
6702                                    // we can take the data without the apk, so we
6703                                    // *want* to do so.  skip the apk by declaring this
6704                                    // one file not-okay without changing the restore
6705                                    // policy for the package.
6706                                    okay = false;
6707                                }
6708                                break;
6709
6710                            default:
6711                                // Something has gone dreadfully wrong when determining
6712                                // the restore policy from the manifest.  Ignore the
6713                                // rest of this package's data.
6714                                Slog.e(TAG, "Invalid policy from manifest");
6715                                okay = false;
6716                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6717                                break;
6718                        }
6719
6720                        // The path needs to be canonical
6721                        if (info.path.contains("..") || info.path.contains("//")) {
6722                            if (MORE_DEBUG) {
6723                                Slog.w(TAG, "Dropping invalid path " + info.path);
6724                            }
6725                            okay = false;
6726                        }
6727
6728                        // If the policy is satisfied, go ahead and set up to pipe the
6729                        // data to the agent.
6730                        if (DEBUG && okay && mAgent != null) {
6731                            Slog.i(TAG, "Reusing existing agent instance");
6732                        }
6733                        if (okay && mAgent == null) {
6734                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
6735
6736                            try {
6737                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
6738
6739                                // If we haven't sent any data to this app yet, we probably
6740                                // need to clear it first.  Check that.
6741                                if (!mClearedPackages.contains(pkg)) {
6742                                    // apps with their own backup agents are
6743                                    // responsible for coherently managing a full
6744                                    // restore.
6745                                    if (mTargetApp.backupAgentName == null) {
6746                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
6747                                        clearApplicationDataSynchronous(pkg);
6748                                    } else {
6749                                        if (DEBUG) Slog.d(TAG, "backup agent ("
6750                                                + mTargetApp.backupAgentName + ") => no clear");
6751                                    }
6752                                    mClearedPackages.add(pkg);
6753                                } else {
6754                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
6755                                }
6756
6757                                // All set; now set up the IPC and launch the agent
6758                                setUpPipes();
6759                                mAgent = bindToAgentSynchronous(mTargetApp,
6760                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
6761                                mAgentPackage = pkg;
6762                            } catch (IOException e) {
6763                                // fall through to error handling
6764                            } catch (NameNotFoundException e) {
6765                                // fall through to error handling
6766                            }
6767
6768                            if (mAgent == null) {
6769                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
6770                                okay = false;
6771                                tearDownPipes();
6772                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6773                            }
6774                        }
6775
6776                        // Sanity check: make sure we never give data to the wrong app.  This
6777                        // should never happen but a little paranoia here won't go amiss.
6778                        if (okay && !pkg.equals(mAgentPackage)) {
6779                            Slog.e(TAG, "Restoring data for " + pkg
6780                                    + " but agent is for " + mAgentPackage);
6781                            okay = false;
6782                        }
6783
6784                        // At this point we have an agent ready to handle the full
6785                        // restore data as well as a pipe for sending data to
6786                        // that agent.  Tell the agent to start reading from the
6787                        // pipe.
6788                        if (okay) {
6789                            boolean agentSuccess = true;
6790                            long toCopy = info.size;
6791                            final int token = generateToken();
6792                            try {
6793                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
6794                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
6795                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
6796                                            + " : " + info.path);
6797                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
6798                                            info.size, info.type, info.path, info.mode,
6799                                            info.mtime, token, mBackupManagerBinder);
6800                                } else {
6801                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
6802                                            + info.path);
6803                                    // fire up the app's agent listening on the socket.  If
6804                                    // the agent is running in the system process we can't
6805                                    // just invoke it asynchronously, so we provide a thread
6806                                    // for it here.
6807                                    if (mTargetApp.processName.equals("system")) {
6808                                        Slog.d(TAG, "system process agent - spinning a thread");
6809                                        RestoreFileRunnable runner = new RestoreFileRunnable(
6810                                                mAgent, info, mPipes[0], token);
6811                                        new Thread(runner, "restore-sys-runner").start();
6812                                    } else {
6813                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
6814                                                info.domain, info.path, info.mode, info.mtime,
6815                                                token, mBackupManagerBinder);
6816                                    }
6817                                }
6818                            } catch (IOException e) {
6819                                // couldn't dup the socket for a process-local restore
6820                                Slog.d(TAG, "Couldn't establish restore");
6821                                agentSuccess = false;
6822                                okay = false;
6823                            } catch (RemoteException e) {
6824                                // whoops, remote entity went away.  We'll eat the content
6825                                // ourselves, then, and not copy it over.
6826                                Slog.e(TAG, "Agent crashed during full restore");
6827                                agentSuccess = false;
6828                                okay = false;
6829                            }
6830
6831                            // Copy over the data if the agent is still good
6832                            if (okay) {
6833                                boolean pipeOkay = true;
6834                                FileOutputStream pipe = new FileOutputStream(
6835                                        mPipes[1].getFileDescriptor());
6836                                while (toCopy > 0) {
6837                                    int toRead = (toCopy > buffer.length)
6838                                    ? buffer.length : (int)toCopy;
6839                                    int nRead = instream.read(buffer, 0, toRead);
6840                                    if (nRead >= 0) mBytes += nRead;
6841                                    if (nRead <= 0) break;
6842                                    toCopy -= nRead;
6843
6844                                    // send it to the output pipe as long as things
6845                                    // are still good
6846                                    if (pipeOkay) {
6847                                        try {
6848                                            pipe.write(buffer, 0, nRead);
6849                                        } catch (IOException e) {
6850                                            Slog.e(TAG, "Failed to write to restore pipe", e);
6851                                            pipeOkay = false;
6852                                        }
6853                                    }
6854                                }
6855
6856                                // done sending that file!  Now we just need to consume
6857                                // the delta from info.size to the end of block.
6858                                skipTarPadding(info.size, instream);
6859
6860                                // and now that we've sent it all, wait for the remote
6861                                // side to acknowledge receipt
6862                                agentSuccess = waitUntilOperationComplete(token);
6863                            }
6864
6865                            // okay, if the remote end failed at any point, deal with
6866                            // it by ignoring the rest of the restore on it
6867                            if (!agentSuccess) {
6868                                mBackupHandler.removeMessages(MSG_TIMEOUT);
6869                                tearDownPipes();
6870                                tearDownAgent(mTargetApp);
6871                                mAgent = null;
6872                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6873                            }
6874                        }
6875
6876                        // Problems setting up the agent communication, or an already-
6877                        // ignored package: skip to the next tar stream entry by
6878                        // reading and discarding this file.
6879                        if (!okay) {
6880                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
6881                            long bytesToConsume = (info.size + 511) & ~511;
6882                            while (bytesToConsume > 0) {
6883                                int toRead = (bytesToConsume > buffer.length)
6884                                ? buffer.length : (int)bytesToConsume;
6885                                long nRead = instream.read(buffer, 0, toRead);
6886                                if (nRead >= 0) mBytes += nRead;
6887                                if (nRead <= 0) break;
6888                                bytesToConsume -= nRead;
6889                            }
6890                        }
6891                    }
6892                }
6893            } catch (IOException e) {
6894                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
6895                // treat as EOF
6896                info = null;
6897            }
6898
6899            return (info != null);
6900        }
6901
6902        void setUpPipes() throws IOException {
6903            mPipes = ParcelFileDescriptor.createPipe();
6904        }
6905
6906        void tearDownPipes() {
6907            if (mPipes != null) {
6908                try {
6909                    mPipes[0].close();
6910                    mPipes[0] = null;
6911                    mPipes[1].close();
6912                    mPipes[1] = null;
6913                } catch (IOException e) {
6914                    Slog.w(TAG, "Couldn't close agent pipes", e);
6915                }
6916                mPipes = null;
6917            }
6918        }
6919
6920        void tearDownAgent(ApplicationInfo app) {
6921            if (mAgent != null) {
6922                try {
6923                    // unbind and tidy up even on timeout or failure, just in case
6924                    mActivityManager.unbindBackupAgent(app);
6925
6926                    // The agent was running with a stub Application object, so shut it down.
6927                    // !!! We hardcode the confirmation UI's package name here rather than use a
6928                    //     manifest flag!  TODO something less direct.
6929                    if (app.uid >= Process.FIRST_APPLICATION_UID
6930                            && !app.packageName.equals("com.android.backupconfirm")) {
6931                        if (DEBUG) Slog.d(TAG, "Killing host process");
6932                        mActivityManager.killApplicationProcess(app.processName, app.uid);
6933                    } else {
6934                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
6935                    }
6936                } catch (RemoteException e) {
6937                    Slog.d(TAG, "Lost app trying to shut down");
6938                }
6939                mAgent = null;
6940            }
6941        }
6942
6943        class RestoreInstallObserver extends PackageInstallObserver {
6944            final AtomicBoolean mDone = new AtomicBoolean();
6945            String mPackageName;
6946            int mResult;
6947
6948            public void reset() {
6949                synchronized (mDone) {
6950                    mDone.set(false);
6951                }
6952            }
6953
6954            public void waitForCompletion() {
6955                synchronized (mDone) {
6956                    while (mDone.get() == false) {
6957                        try {
6958                            mDone.wait();
6959                        } catch (InterruptedException e) { }
6960                    }
6961                }
6962            }
6963
6964            int getResult() {
6965                return mResult;
6966            }
6967
6968            @Override
6969            public void onPackageInstalled(String packageName, int returnCode,
6970                    String msg, Bundle extras) {
6971                synchronized (mDone) {
6972                    mResult = returnCode;
6973                    mPackageName = packageName;
6974                    mDone.set(true);
6975                    mDone.notifyAll();
6976                }
6977            }
6978        }
6979
6980        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
6981            final AtomicBoolean mDone = new AtomicBoolean();
6982            int mResult;
6983
6984            public void reset() {
6985                synchronized (mDone) {
6986                    mDone.set(false);
6987                }
6988            }
6989
6990            public void waitForCompletion() {
6991                synchronized (mDone) {
6992                    while (mDone.get() == false) {
6993                        try {
6994                            mDone.wait();
6995                        } catch (InterruptedException e) { }
6996                    }
6997                }
6998            }
6999
7000            @Override
7001            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
7002                synchronized (mDone) {
7003                    mResult = returnCode;
7004                    mDone.set(true);
7005                    mDone.notifyAll();
7006                }
7007            }
7008        }
7009
7010        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
7011        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
7012
7013        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
7014            boolean okay = true;
7015
7016            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
7017
7018            // The file content is an .apk file.  Copy it out to a staging location and
7019            // attempt to install it.
7020            File apkFile = new File(mDataDir, info.packageName);
7021            try {
7022                FileOutputStream apkStream = new FileOutputStream(apkFile);
7023                byte[] buffer = new byte[32 * 1024];
7024                long size = info.size;
7025                while (size > 0) {
7026                    long toRead = (buffer.length < size) ? buffer.length : size;
7027                    int didRead = instream.read(buffer, 0, (int)toRead);
7028                    if (didRead >= 0) mBytes += didRead;
7029                    apkStream.write(buffer, 0, didRead);
7030                    size -= didRead;
7031                }
7032                apkStream.close();
7033
7034                // make sure the installer can read it
7035                apkFile.setReadable(true, false);
7036
7037                // Now install it
7038                Uri packageUri = Uri.fromFile(apkFile);
7039                mInstallObserver.reset();
7040                mPackageManager.installPackage(packageUri, mInstallObserver,
7041                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
7042                        installerPackage);
7043                mInstallObserver.waitForCompletion();
7044
7045                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
7046                    // The only time we continue to accept install of data even if the
7047                    // apk install failed is if we had already determined that we could
7048                    // accept the data regardless.
7049                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
7050                        okay = false;
7051                    }
7052                } else {
7053                    // Okay, the install succeeded.  Make sure it was the right app.
7054                    boolean uninstall = false;
7055                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
7056                        Slog.w(TAG, "Restore stream claimed to include apk for "
7057                                + info.packageName + " but apk was really "
7058                                + mInstallObserver.mPackageName);
7059                        // delete the package we just put in place; it might be fraudulent
7060                        okay = false;
7061                        uninstall = true;
7062                    } else {
7063                        try {
7064                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
7065                                    PackageManager.GET_SIGNATURES);
7066                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
7067                                Slog.w(TAG, "Restore stream contains apk of package "
7068                                        + info.packageName + " but it disallows backup/restore");
7069                                okay = false;
7070                            } else {
7071                                // So far so good -- do the signatures match the manifest?
7072                                Signature[] sigs = mManifestSignatures.get(info.packageName);
7073                                if (signaturesMatch(sigs, pkg)) {
7074                                    // If this is a system-uid app without a declared backup agent,
7075                                    // don't restore any of the file data.
7076                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
7077                                            && (pkg.applicationInfo.backupAgentName == null)) {
7078                                        Slog.w(TAG, "Installed app " + info.packageName
7079                                                + " has restricted uid and no agent");
7080                                        okay = false;
7081                                    }
7082                                } else {
7083                                    Slog.w(TAG, "Installed app " + info.packageName
7084                                            + " signatures do not match restore manifest");
7085                                    okay = false;
7086                                    uninstall = true;
7087                                }
7088                            }
7089                        } catch (NameNotFoundException e) {
7090                            Slog.w(TAG, "Install of package " + info.packageName
7091                                    + " succeeded but now not found");
7092                            okay = false;
7093                        }
7094                    }
7095
7096                    // If we're not okay at this point, we need to delete the package
7097                    // that we just installed.
7098                    if (uninstall) {
7099                        mDeleteObserver.reset();
7100                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
7101                                mDeleteObserver, 0);
7102                        mDeleteObserver.waitForCompletion();
7103                    }
7104                }
7105            } catch (IOException e) {
7106                Slog.e(TAG, "Unable to transcribe restored apk for install");
7107                okay = false;
7108            } finally {
7109                apkFile.delete();
7110            }
7111
7112            return okay;
7113        }
7114
7115        // Given an actual file content size, consume the post-content padding mandated
7116        // by the tar format.
7117        void skipTarPadding(long size, InputStream instream) throws IOException {
7118            long partial = (size + 512) % 512;
7119            if (partial > 0) {
7120                final int needed = 512 - (int)partial;
7121                byte[] buffer = new byte[needed];
7122                if (readExactly(instream, buffer, 0, needed) == needed) {
7123                    mBytes += needed;
7124                } else throw new IOException("Unexpected EOF in padding");
7125            }
7126        }
7127
7128        // Read a widget metadata file, returning the restored blob
7129        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
7130            // Fail on suspiciously large widget dump files
7131            if (info.size > 64 * 1024) {
7132                throw new IOException("Metadata too big; corrupt? size=" + info.size);
7133            }
7134
7135            byte[] buffer = new byte[(int) info.size];
7136            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
7137                mBytes += info.size;
7138            } else throw new IOException("Unexpected EOF in widget data");
7139
7140            String[] str = new String[1];
7141            int offset = extractLine(buffer, 0, str);
7142            int version = Integer.parseInt(str[0]);
7143            if (version == BACKUP_MANIFEST_VERSION) {
7144                offset = extractLine(buffer, offset, str);
7145                final String pkg = str[0];
7146                if (info.packageName.equals(pkg)) {
7147                    // Data checks out -- the rest of the buffer is a concatenation of
7148                    // binary blobs as described in the comment at writeAppWidgetData()
7149                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
7150                            offset, buffer.length - offset);
7151                    DataInputStream in = new DataInputStream(bin);
7152                    while (bin.available() > 0) {
7153                        int token = in.readInt();
7154                        int size = in.readInt();
7155                        if (size > 64 * 1024) {
7156                            throw new IOException("Datum "
7157                                    + Integer.toHexString(token)
7158                                    + " too big; corrupt? size=" + info.size);
7159                        }
7160                        switch (token) {
7161                            case BACKUP_WIDGET_METADATA_TOKEN:
7162                            {
7163                                if (MORE_DEBUG) {
7164                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
7165                                }
7166                                mWidgetData = new byte[size];
7167                                in.read(mWidgetData);
7168                                break;
7169                            }
7170                            default:
7171                            {
7172                                if (DEBUG) {
7173                                    Slog.i(TAG, "Ignoring metadata blob "
7174                                            + Integer.toHexString(token)
7175                                            + " for " + info.packageName);
7176                                }
7177                                in.skipBytes(size);
7178                                break;
7179                            }
7180                        }
7181                    }
7182                } else {
7183                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
7184                            + " but widget data for " + pkg);
7185                }
7186            } else {
7187                Slog.w(TAG, "Unsupported metadata version " + version);
7188            }
7189        }
7190
7191        // Returns a policy constant; takes a buffer arg to reduce memory churn
7192        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
7193                throws IOException {
7194            // Fail on suspiciously large manifest files
7195            if (info.size > 64 * 1024) {
7196                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
7197            }
7198
7199            byte[] buffer = new byte[(int) info.size];
7200            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
7201                mBytes += info.size;
7202            } else throw new IOException("Unexpected EOF in manifest");
7203
7204            RestorePolicy policy = RestorePolicy.IGNORE;
7205            String[] str = new String[1];
7206            int offset = 0;
7207
7208            try {
7209                offset = extractLine(buffer, offset, str);
7210                int version = Integer.parseInt(str[0]);
7211                if (version == BACKUP_MANIFEST_VERSION) {
7212                    offset = extractLine(buffer, offset, str);
7213                    String manifestPackage = str[0];
7214                    // TODO: handle <original-package>
7215                    if (manifestPackage.equals(info.packageName)) {
7216                        offset = extractLine(buffer, offset, str);
7217                        version = Integer.parseInt(str[0]);  // app version
7218                        offset = extractLine(buffer, offset, str);
7219                        // This is the platform version, which we don't use, but we parse it
7220                        // as a safety against corruption in the manifest.
7221                        Integer.parseInt(str[0]);
7222                        offset = extractLine(buffer, offset, str);
7223                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
7224                        offset = extractLine(buffer, offset, str);
7225                        boolean hasApk = str[0].equals("1");
7226                        offset = extractLine(buffer, offset, str);
7227                        int numSigs = Integer.parseInt(str[0]);
7228                        if (numSigs > 0) {
7229                            Signature[] sigs = new Signature[numSigs];
7230                            for (int i = 0; i < numSigs; i++) {
7231                                offset = extractLine(buffer, offset, str);
7232                                sigs[i] = new Signature(str[0]);
7233                            }
7234                            mManifestSignatures.put(info.packageName, sigs);
7235
7236                            // Okay, got the manifest info we need...
7237                            try {
7238                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
7239                                        info.packageName, PackageManager.GET_SIGNATURES);
7240                                // Fall through to IGNORE if the app explicitly disallows backup
7241                                final int flags = pkgInfo.applicationInfo.flags;
7242                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
7243                                    // Restore system-uid-space packages only if they have
7244                                    // defined a custom backup agent
7245                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
7246                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
7247                                        // Verify signatures against any installed version; if they
7248                                        // don't match, then we fall though and ignore the data.  The
7249                                        // signatureMatch() method explicitly ignores the signature
7250                                        // check for packages installed on the system partition, because
7251                                        // such packages are signed with the platform cert instead of
7252                                        // the app developer's cert, so they're different on every
7253                                        // device.
7254                                        if (signaturesMatch(sigs, pkgInfo)) {
7255                                            if (pkgInfo.versionCode >= version) {
7256                                                Slog.i(TAG, "Sig + version match; taking data");
7257                                                policy = RestorePolicy.ACCEPT;
7258                                            } else {
7259                                                // The data is from a newer version of the app than
7260                                                // is presently installed.  That means we can only
7261                                                // use it if the matching apk is also supplied.
7262                                                Slog.d(TAG, "Data version " + version
7263                                                        + " is newer than installed version "
7264                                                        + pkgInfo.versionCode + " - requiring apk");
7265                                                policy = RestorePolicy.ACCEPT_IF_APK;
7266                                            }
7267                                        } else {
7268                                            Slog.w(TAG, "Restore manifest signatures do not match "
7269                                                    + "installed application for " + info.packageName);
7270                                        }
7271                                    } else {
7272                                        Slog.w(TAG, "Package " + info.packageName
7273                                                + " is system level with no agent");
7274                                    }
7275                                } else {
7276                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
7277                                            + info.packageName + " but allowBackup=false");
7278                                }
7279                            } catch (NameNotFoundException e) {
7280                                // Okay, the target app isn't installed.  We can process
7281                                // the restore properly only if the dataset provides the
7282                                // apk file and we can successfully install it.
7283                                if (DEBUG) Slog.i(TAG, "Package " + info.packageName
7284                                        + " not installed; requiring apk in dataset");
7285                                policy = RestorePolicy.ACCEPT_IF_APK;
7286                            }
7287
7288                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
7289                                Slog.i(TAG, "Cannot restore package " + info.packageName
7290                                        + " without the matching .apk");
7291                            }
7292                        } else {
7293                            Slog.i(TAG, "Missing signature on backed-up package "
7294                                    + info.packageName);
7295                        }
7296                    } else {
7297                        Slog.i(TAG, "Expected package " + info.packageName
7298                                + " but restore manifest claims " + manifestPackage);
7299                    }
7300                } else {
7301                    Slog.i(TAG, "Unknown restore manifest version " + version
7302                            + " for package " + info.packageName);
7303                }
7304            } catch (NumberFormatException e) {
7305                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
7306            } catch (IllegalArgumentException e) {
7307                Slog.w(TAG, e.getMessage());
7308            }
7309
7310            return policy;
7311        }
7312
7313        // Builds a line from a byte buffer starting at 'offset', and returns
7314        // the index of the next unconsumed data in the buffer.
7315        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
7316            final int end = buffer.length;
7317            if (offset >= end) throw new IOException("Incomplete data");
7318
7319            int pos;
7320            for (pos = offset; pos < end; pos++) {
7321                byte c = buffer[pos];
7322                // at LF we declare end of line, and return the next char as the
7323                // starting point for the next time through
7324                if (c == '\n') {
7325                    break;
7326                }
7327            }
7328            outStr[0] = new String(buffer, offset, pos - offset);
7329            pos++;  // may be pointing an extra byte past the end but that's okay
7330            return pos;
7331        }
7332
7333        void dumpFileMetadata(FileMetadata info) {
7334            if (DEBUG) {
7335                StringBuilder b = new StringBuilder(128);
7336
7337                // mode string
7338                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
7339                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
7340                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
7341                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
7342                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
7343                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
7344                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
7345                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
7346                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
7347                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
7348                b.append(String.format(" %9d ", info.size));
7349
7350                Date stamp = new Date(info.mtime);
7351                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
7352
7353                b.append(info.packageName);
7354                b.append(" :: ");
7355                b.append(info.domain);
7356                b.append(" :: ");
7357                b.append(info.path);
7358
7359                Slog.i(TAG, b.toString());
7360            }
7361        }
7362        // Consume a tar file header block [sequence] and accumulate the relevant metadata
7363        FileMetadata readTarHeaders(InputStream instream) throws IOException {
7364            byte[] block = new byte[512];
7365            FileMetadata info = null;
7366
7367            boolean gotHeader = readTarHeader(instream, block);
7368            if (gotHeader) {
7369                try {
7370                    // okay, presume we're okay, and extract the various metadata
7371                    info = new FileMetadata();
7372                    info.size = extractRadix(block, 124, 12, 8);
7373                    info.mtime = extractRadix(block, 136, 12, 8);
7374                    info.mode = extractRadix(block, 100, 8, 8);
7375
7376                    info.path = extractString(block, 345, 155); // prefix
7377                    String path = extractString(block, 0, 100);
7378                    if (path.length() > 0) {
7379                        if (info.path.length() > 0) info.path += '/';
7380                        info.path += path;
7381                    }
7382
7383                    // tar link indicator field: 1 byte at offset 156 in the header.
7384                    int typeChar = block[156];
7385                    if (typeChar == 'x') {
7386                        // pax extended header, so we need to read that
7387                        gotHeader = readPaxExtendedHeader(instream, info);
7388                        if (gotHeader) {
7389                            // and after a pax extended header comes another real header -- read
7390                            // that to find the real file type
7391                            gotHeader = readTarHeader(instream, block);
7392                        }
7393                        if (!gotHeader) throw new IOException("Bad or missing pax header");
7394
7395                        typeChar = block[156];
7396                    }
7397
7398                    switch (typeChar) {
7399                        case '0': info.type = BackupAgent.TYPE_FILE; break;
7400                        case '5': {
7401                            info.type = BackupAgent.TYPE_DIRECTORY;
7402                            if (info.size != 0) {
7403                                Slog.w(TAG, "Directory entry with nonzero size in header");
7404                                info.size = 0;
7405                            }
7406                            break;
7407                        }
7408                        case 0: {
7409                            // presume EOF
7410                            if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
7411                            return null;
7412                        }
7413                        default: {
7414                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
7415                            throw new IOException("Unknown entity type " + typeChar);
7416                        }
7417                    }
7418
7419                    // Parse out the path
7420                    //
7421                    // first: apps/shared/unrecognized
7422                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
7423                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
7424                        // File in shared storage.  !!! TODO: implement this.
7425                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
7426                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
7427                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
7428                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
7429                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
7430                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
7431                        // App content!  Parse out the package name and domain
7432
7433                        // strip the apps/ prefix
7434                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
7435
7436                        // extract the package name
7437                        int slash = info.path.indexOf('/');
7438                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
7439                        info.packageName = info.path.substring(0, slash);
7440                        info.path = info.path.substring(slash+1);
7441
7442                        // if it's a manifest or metadata payload we're done, otherwise parse
7443                        // out the domain into which the file will be restored
7444                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
7445                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
7446                            slash = info.path.indexOf('/');
7447                            if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
7448                            info.domain = info.path.substring(0, slash);
7449                            info.path = info.path.substring(slash + 1);
7450                        }
7451                    }
7452                } catch (IOException e) {
7453                    if (DEBUG) {
7454                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
7455                        HEXLOG(block);
7456                    }
7457                    throw e;
7458                }
7459            }
7460            return info;
7461        }
7462
7463        private void HEXLOG(byte[] block) {
7464            int offset = 0;
7465            int todo = block.length;
7466            StringBuilder buf = new StringBuilder(64);
7467            while (todo > 0) {
7468                buf.append(String.format("%04x   ", offset));
7469                int numThisLine = (todo > 16) ? 16 : todo;
7470                for (int i = 0; i < numThisLine; i++) {
7471                    buf.append(String.format("%02x ", block[offset+i]));
7472                }
7473                Slog.i("hexdump", buf.toString());
7474                buf.setLength(0);
7475                todo -= numThisLine;
7476                offset += numThisLine;
7477            }
7478        }
7479
7480        // Read exactly the given number of bytes into a buffer at the stated offset.
7481        // Returns false if EOF is encountered before the requested number of bytes
7482        // could be read.
7483        int readExactly(InputStream in, byte[] buffer, int offset, int size)
7484                throws IOException {
7485            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
7486
7487            int soFar = 0;
7488            while (soFar < size) {
7489                int nRead = in.read(buffer, offset + soFar, size - soFar);
7490                if (nRead <= 0) {
7491                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
7492                    break;
7493                }
7494                soFar += nRead;
7495            }
7496            return soFar;
7497        }
7498
7499        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
7500            final int got = readExactly(instream, block, 0, 512);
7501            if (got == 0) return false;     // Clean EOF
7502            if (got < 512) throw new IOException("Unable to read full block header");
7503            mBytes += 512;
7504            return true;
7505        }
7506
7507        // overwrites 'info' fields based on the pax extended header
7508        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
7509                throws IOException {
7510            // We should never see a pax extended header larger than this
7511            if (info.size > 32*1024) {
7512                Slog.w(TAG, "Suspiciously large pax header size " + info.size
7513                        + " - aborting");
7514                throw new IOException("Sanity failure: pax header size " + info.size);
7515            }
7516
7517            // read whole blocks, not just the content size
7518            int numBlocks = (int)((info.size + 511) >> 9);
7519            byte[] data = new byte[numBlocks * 512];
7520            if (readExactly(instream, data, 0, data.length) < data.length) {
7521                throw new IOException("Unable to read full pax header");
7522            }
7523            mBytes += data.length;
7524
7525            final int contentSize = (int) info.size;
7526            int offset = 0;
7527            do {
7528                // extract the line at 'offset'
7529                int eol = offset+1;
7530                while (eol < contentSize && data[eol] != ' ') eol++;
7531                if (eol >= contentSize) {
7532                    // error: we just hit EOD looking for the end of the size field
7533                    throw new IOException("Invalid pax data");
7534                }
7535                // eol points to the space between the count and the key
7536                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
7537                int key = eol + 1;  // start of key=value
7538                eol = offset + linelen - 1; // trailing LF
7539                int value;
7540                for (value = key+1; data[value] != '=' && value <= eol; value++);
7541                if (value > eol) {
7542                    throw new IOException("Invalid pax declaration");
7543                }
7544
7545                // pax requires that key/value strings be in UTF-8
7546                String keyStr = new String(data, key, value-key, "UTF-8");
7547                // -1 to strip the trailing LF
7548                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
7549
7550                if ("path".equals(keyStr)) {
7551                    info.path = valStr;
7552                } else if ("size".equals(keyStr)) {
7553                    info.size = Long.parseLong(valStr);
7554                } else {
7555                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
7556                }
7557
7558                offset += linelen;
7559            } while (offset < contentSize);
7560
7561            return true;
7562        }
7563
7564        long extractRadix(byte[] data, int offset, int maxChars, int radix)
7565                throws IOException {
7566            long value = 0;
7567            final int end = offset + maxChars;
7568            for (int i = offset; i < end; i++) {
7569                final byte b = data[i];
7570                // Numeric fields in tar can terminate with either NUL or SPC
7571                if (b == 0 || b == ' ') break;
7572                if (b < '0' || b > ('0' + radix - 1)) {
7573                    throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix);
7574                }
7575                value = radix * value + (b - '0');
7576            }
7577            return value;
7578        }
7579
7580        String extractString(byte[] data, int offset, int maxChars) throws IOException {
7581            final int end = offset + maxChars;
7582            int eos = offset;
7583            // tar string fields terminate early with a NUL
7584            while (eos < end && data[eos] != 0) eos++;
7585            return new String(data, offset, eos-offset, "US-ASCII");
7586        }
7587
7588        void sendStartRestore() {
7589            if (mObserver != null) {
7590                try {
7591                    mObserver.onStartRestore();
7592                } catch (RemoteException e) {
7593                    Slog.w(TAG, "full restore observer went away: startRestore");
7594                    mObserver = null;
7595                }
7596            }
7597        }
7598
7599        void sendOnRestorePackage(String name) {
7600            if (mObserver != null) {
7601                try {
7602                    // TODO: use a more user-friendly name string
7603                    mObserver.onRestorePackage(name);
7604                } catch (RemoteException e) {
7605                    Slog.w(TAG, "full restore observer went away: restorePackage");
7606                    mObserver = null;
7607                }
7608            }
7609        }
7610
7611        void sendEndRestore() {
7612            if (mObserver != null) {
7613                try {
7614                    mObserver.onEndRestore();
7615                } catch (RemoteException e) {
7616                    Slog.w(TAG, "full restore observer went away: endRestore");
7617                    mObserver = null;
7618                }
7619            }
7620        }
7621    }
7622
7623    // ----- Restore handling -----
7624
7625    // Old style: directly match the stored vs on device signature blocks
7626    static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
7627        if (target == null) {
7628            return false;
7629        }
7630
7631        // If the target resides on the system partition, we allow it to restore
7632        // data from the like-named package in a restore set even if the signatures
7633        // do not match.  (Unlike general applications, those flashed to the system
7634        // partition will be signed with the device's platform certificate, so on
7635        // different phones the same system app will have different signatures.)
7636        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
7637            if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
7638            return true;
7639        }
7640
7641        // Allow unsigned apps, but not signed on one device and unsigned on the other
7642        // !!! TODO: is this the right policy?
7643        Signature[] deviceSigs = target.signatures;
7644        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
7645                + " device=" + deviceSigs);
7646        if ((storedSigs == null || storedSigs.length == 0)
7647                && (deviceSigs == null || deviceSigs.length == 0)) {
7648            return true;
7649        }
7650        if (storedSigs == null || deviceSigs == null) {
7651            return false;
7652        }
7653
7654        // !!! TODO: this demands that every stored signature match one
7655        // that is present on device, and does not demand the converse.
7656        // Is this this right policy?
7657        int nStored = storedSigs.length;
7658        int nDevice = deviceSigs.length;
7659
7660        for (int i=0; i < nStored; i++) {
7661            boolean match = false;
7662            for (int j=0; j < nDevice; j++) {
7663                if (storedSigs[i].equals(deviceSigs[j])) {
7664                    match = true;
7665                    break;
7666                }
7667            }
7668            if (!match) {
7669                return false;
7670            }
7671        }
7672        return true;
7673    }
7674
7675    // Used by both incremental and full restore
7676    void restoreWidgetData(String packageName, byte[] widgetData) {
7677        // Apply the restored widget state and generate the ID update for the app
7678        // TODO: http://b/22388012
7679        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
7680    }
7681
7682    // *****************************
7683    // NEW UNIFIED RESTORE IMPLEMENTATION
7684    // *****************************
7685
7686    // states of the unified-restore state machine
7687    enum UnifiedRestoreState {
7688        INITIAL,
7689        RUNNING_QUEUE,
7690        RESTORE_KEYVALUE,
7691        RESTORE_FULL,
7692        RESTORE_FINISHED,
7693        FINAL
7694    }
7695
7696    class PerformUnifiedRestoreTask implements BackupRestoreTask {
7697        // Transport we're working with to do the restore
7698        private IBackupTransport mTransport;
7699
7700        // Where per-transport saved state goes
7701        File mStateDir;
7702
7703        // Restore observer; may be null
7704        private IRestoreObserver mObserver;
7705
7706        // Token identifying the dataset to the transport
7707        private long mToken;
7708
7709        // When this is a restore-during-install, this is the token identifying the
7710        // operation to the Package Manager, and we must ensure that we let it know
7711        // when we're finished.
7712        private int mPmToken;
7713
7714        // When this is restore-during-install, we need to tell the package manager
7715        // whether we actually launched the app, because this affects notifications
7716        // around externally-visible state transitions.
7717        private boolean mDidLaunch;
7718
7719        // Is this a whole-system restore, i.e. are we establishing a new ancestral
7720        // dataset to base future restore-at-install operations from?
7721        private boolean mIsSystemRestore;
7722
7723        // If this is a single-package restore, what package are we interested in?
7724        private PackageInfo mTargetPackage;
7725
7726        // In all cases, the calculated list of packages that we are trying to restore
7727        private List<PackageInfo> mAcceptSet;
7728
7729        // Our bookkeeping about the ancestral dataset
7730        private PackageManagerBackupAgent mPmAgent;
7731
7732        // Currently-bound backup agent for restore + restoreFinished purposes
7733        private IBackupAgent mAgent;
7734
7735        // What sort of restore we're doing now
7736        private RestoreDescription mRestoreDescription;
7737
7738        // The package we're currently restoring
7739        private PackageInfo mCurrentPackage;
7740
7741        // Widget-related data handled as part of this restore operation
7742        private byte[] mWidgetData;
7743
7744        // Number of apps restored in this pass
7745        private int mCount;
7746
7747        // When did we start?
7748        private long mStartRealtime;
7749
7750        // State machine progress
7751        private UnifiedRestoreState mState;
7752
7753        // How are things going?
7754        private int mStatus;
7755
7756        // Done?
7757        private boolean mFinished;
7758
7759        // Key/value: bookkeeping about staged data and files for agent access
7760        private File mBackupDataName;
7761        private File mStageName;
7762        private File mSavedStateName;
7763        private File mNewStateName;
7764        ParcelFileDescriptor mBackupData;
7765        ParcelFileDescriptor mNewState;
7766
7767        // Invariant: mWakelock is already held, and this task is responsible for
7768        // releasing it at the end of the restore operation.
7769        PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer,
7770                long restoreSetToken, PackageInfo targetPackage, int pmToken,
7771                boolean isFullSystemRestore, String[] filterSet) {
7772            mState = UnifiedRestoreState.INITIAL;
7773            mStartRealtime = SystemClock.elapsedRealtime();
7774
7775            mTransport = transport;
7776            mObserver = observer;
7777            mToken = restoreSetToken;
7778            mPmToken = pmToken;
7779            mTargetPackage = targetPackage;
7780            mIsSystemRestore = isFullSystemRestore;
7781            mFinished = false;
7782            mDidLaunch = false;
7783
7784            if (targetPackage != null) {
7785                // Single package restore
7786                mAcceptSet = new ArrayList<PackageInfo>();
7787                mAcceptSet.add(targetPackage);
7788            } else {
7789                // Everything possible, or a target set
7790                if (filterSet == null) {
7791                    // We want everything and a pony
7792                    List<PackageInfo> apps =
7793                            PackageManagerBackupAgent.getStorableApplications(mPackageManager);
7794                    filterSet = packagesToNames(apps);
7795                    if (DEBUG) {
7796                        Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
7797                    }
7798                }
7799
7800                mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
7801
7802                // Pro tem, we insist on moving the settings provider package to last place.
7803                // Keep track of whether it's in the list, and bump it down if so.  We also
7804                // want to do the system package itself first if it's called for.
7805                boolean hasSystem = false;
7806                boolean hasSettings = false;
7807                for (int i = 0; i < filterSet.length; i++) {
7808                    try {
7809                        PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
7810                        if ("android".equals(info.packageName)) {
7811                            hasSystem = true;
7812                            continue;
7813                        }
7814                        if (SETTINGS_PACKAGE.equals(info.packageName)) {
7815                            hasSettings = true;
7816                            continue;
7817                        }
7818
7819                        if (appIsEligibleForBackup(info.applicationInfo)) {
7820                            mAcceptSet.add(info);
7821                        }
7822                    } catch (NameNotFoundException e) {
7823                        // requested package name doesn't exist; ignore it
7824                    }
7825                }
7826                if (hasSystem) {
7827                    try {
7828                        mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
7829                    } catch (NameNotFoundException e) {
7830                        // won't happen; we know a priori that it's valid
7831                    }
7832                }
7833                if (hasSettings) {
7834                    try {
7835                        mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
7836                    } catch (NameNotFoundException e) {
7837                        // this one is always valid too
7838                    }
7839                }
7840            }
7841
7842            if (MORE_DEBUG) {
7843                Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
7844                for (PackageInfo info : mAcceptSet) {
7845                    Slog.v(TAG, "   " + info.packageName);
7846                }
7847            }
7848        }
7849
7850        private String[] packagesToNames(List<PackageInfo> apps) {
7851            final int N = apps.size();
7852            String[] names = new String[N];
7853            for (int i = 0; i < N; i++) {
7854                names[i] = apps.get(i).packageName;
7855            }
7856            return names;
7857        }
7858
7859        // Execute one tick of whatever state machine the task implements
7860        @Override
7861        public void execute() {
7862            if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState);
7863            switch (mState) {
7864                case INITIAL:
7865                    startRestore();
7866                    break;
7867
7868                case RUNNING_QUEUE:
7869                    dispatchNextRestore();
7870                    break;
7871
7872                case RESTORE_KEYVALUE:
7873                    restoreKeyValue();
7874                    break;
7875
7876                case RESTORE_FULL:
7877                    restoreFull();
7878                    break;
7879
7880                case RESTORE_FINISHED:
7881                    restoreFinished();
7882                    break;
7883
7884                case FINAL:
7885                    if (!mFinished) finalizeRestore();
7886                    else {
7887                        Slog.e(TAG, "Duplicate finish");
7888                    }
7889                    mFinished = true;
7890                    break;
7891            }
7892        }
7893
7894        /*
7895         * SKETCH OF OPERATION
7896         *
7897         * create one of these PerformUnifiedRestoreTask objects, telling it which
7898         * dataset & transport to address, and then parameters within the restore
7899         * operation: single target package vs many, etc.
7900         *
7901         * 1. transport.startRestore(token, list-of-packages).  If we need @pm@  it is
7902         * always placed first and the settings provider always placed last [for now].
7903         *
7904         * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline]
7905         *
7906         *   [ state change => RUNNING_QUEUE ]
7907         *
7908         * NOW ITERATE:
7909         *
7910         * { 3. t.nextRestorePackage()
7911         *   4. does the metadata for this package allow us to restore it?
7912         *      does the on-disk app permit us to restore it? [re-check allowBackup etc]
7913         *   5. is this a key/value dataset?  => key/value agent restore
7914         *       [ state change => RESTORE_KEYVALUE ]
7915         *       5a. spin up agent
7916         *       5b. t.getRestoreData() to stage it properly
7917         *       5c. call into agent to perform restore
7918         *       5d. tear down agent
7919         *       [ state change => RUNNING_QUEUE ]
7920         *
7921         *   6. else it's a stream dataset:
7922         *       [ state change => RESTORE_FULL ]
7923         *       6a. instantiate the engine for a stream restore: engine handles agent lifecycles
7924         *       6b. spin off engine runner on separate thread
7925         *       6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket
7926         *       [ state change => RUNNING_QUEUE ]
7927         * }
7928         *
7929         *   [ state change => FINAL ]
7930         *
7931         * 7. t.finishRestore(), release wakelock, etc.
7932         *
7933         *
7934         */
7935
7936        // state INITIAL : set up for the restore and read the metadata if necessary
7937        private  void startRestore() {
7938            sendStartRestore(mAcceptSet.size());
7939
7940            // If we're starting a full-system restore, set up to begin widget ID remapping
7941            if (mIsSystemRestore) {
7942                // TODO: http://b/22388012
7943                AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM);
7944            }
7945
7946            try {
7947                String transportDir = mTransport.transportDirName();
7948                mStateDir = new File(mBaseStateDir, transportDir);
7949
7950                // Fetch the current metadata from the dataset first
7951                PackageInfo pmPackage = new PackageInfo();
7952                pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7953                mAcceptSet.add(0, pmPackage);
7954
7955                PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
7956                mStatus = mTransport.startRestore(mToken, packages);
7957                if (mStatus != BackupTransport.TRANSPORT_OK) {
7958                    Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
7959                    mStatus = BackupTransport.TRANSPORT_ERROR;
7960                    executeNextState(UnifiedRestoreState.FINAL);
7961                    return;
7962                }
7963
7964                RestoreDescription desc = mTransport.nextRestorePackage();
7965                if (desc == null) {
7966                    Slog.e(TAG, "No restore metadata available; halting");
7967                    mStatus = BackupTransport.TRANSPORT_ERROR;
7968                    executeNextState(UnifiedRestoreState.FINAL);
7969                    return;
7970                }
7971                if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
7972                    Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
7973                    mStatus = BackupTransport.TRANSPORT_ERROR;
7974                    executeNextState(UnifiedRestoreState.FINAL);
7975                    return;
7976                }
7977
7978                // Pull the Package Manager metadata from the restore set first
7979                mCurrentPackage = new PackageInfo();
7980                mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7981                mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
7982                mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
7983                if (MORE_DEBUG) {
7984                    Slog.v(TAG, "initiating restore for PMBA");
7985                }
7986                initiateOneRestore(mCurrentPackage, 0);
7987                // The PM agent called operationComplete() already, because our invocation
7988                // of it is process-local and therefore synchronous.  That means that the
7989                // next-state message (RUNNING_QUEUE) is already enqueued.  Only if we're
7990                // unable to proceed with running the queue do we remove that pending
7991                // message and jump straight to the FINAL state.  Because this was
7992                // synchronous we also know that we should cancel the pending timeout
7993                // message.
7994                mBackupHandler.removeMessages(MSG_TIMEOUT);
7995
7996                // Verify that the backup set includes metadata.  If not, we can't do
7997                // signature/version verification etc, so we simply do not proceed with
7998                // the restore operation.
7999                if (!mPmAgent.hasMetadata()) {
8000                    Slog.e(TAG, "No restore metadata available, so not restoring");
8001                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8002                            PACKAGE_MANAGER_SENTINEL,
8003                            "Package manager restore metadata missing");
8004                    mStatus = BackupTransport.TRANSPORT_ERROR;
8005                    mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
8006                    executeNextState(UnifiedRestoreState.FINAL);
8007                    return;
8008                }
8009
8010                // Success; cache the metadata and continue as expected with the
8011                // next state already enqueued
8012
8013            } catch (RemoteException e) {
8014                // If we lost the transport at any time, halt
8015                Slog.e(TAG, "Unable to contact transport for restore");
8016                mStatus = BackupTransport.TRANSPORT_ERROR;
8017                mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
8018                executeNextState(UnifiedRestoreState.FINAL);
8019                return;
8020            }
8021        }
8022
8023        // state RUNNING_QUEUE : figure out what the next thing to be restored is,
8024        // and fire the appropriate next step
8025        private void dispatchNextRestore() {
8026            UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
8027            try {
8028                mRestoreDescription = mTransport.nextRestorePackage();
8029                final String pkgName = (mRestoreDescription != null)
8030                        ? mRestoreDescription.getPackageName() : null;
8031                if (pkgName == null) {
8032                    Slog.e(TAG, "Failure getting next package name");
8033                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8034                    nextState = UnifiedRestoreState.FINAL;
8035                    return;
8036                } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) {
8037                    // Yay we've reached the end cleanly
8038                    if (DEBUG) {
8039                        Slog.v(TAG, "No more packages; finishing restore");
8040                    }
8041                    int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
8042                    EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
8043                    nextState = UnifiedRestoreState.FINAL;
8044                    return;
8045                }
8046
8047                if (DEBUG) {
8048                    Slog.i(TAG, "Next restore package: " + mRestoreDescription);
8049                }
8050                sendOnRestorePackage(pkgName);
8051
8052                Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName);
8053                if (metaInfo == null) {
8054                    Slog.e(TAG, "No metadata for " + pkgName);
8055                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
8056                            "Package metadata missing");
8057                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8058                    return;
8059                }
8060
8061                try {
8062                    mCurrentPackage = mPackageManager.getPackageInfo(
8063                            pkgName, PackageManager.GET_SIGNATURES);
8064                } catch (NameNotFoundException e) {
8065                    // Whoops, we thought we could restore this package but it
8066                    // turns out not to be present.  Skip it.
8067                    Slog.e(TAG, "Package not present: " + pkgName);
8068                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
8069                            "Package missing on device");
8070                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8071                    return;
8072                }
8073
8074                if (metaInfo.versionCode > mCurrentPackage.versionCode) {
8075                    // Data is from a "newer" version of the app than we have currently
8076                    // installed.  If the app has not declared that it is prepared to
8077                    // handle this case, we do not attempt the restore.
8078                    if ((mCurrentPackage.applicationInfo.flags
8079                            & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
8080                        String message = "Version " + metaInfo.versionCode
8081                                + " > installed version " + mCurrentPackage.versionCode;
8082                        Slog.w(TAG, "Package " + pkgName + ": " + message);
8083                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8084                                pkgName, message);
8085                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
8086                        return;
8087                    } else {
8088                        if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
8089                                + " > installed " + mCurrentPackage.versionCode
8090                                + " but restoreAnyVersion");
8091                    }
8092                }
8093
8094                if (MORE_DEBUG) Slog.v(TAG, "Package " + pkgName
8095                        + " restore version [" + metaInfo.versionCode
8096                        + "] is compatible with installed version ["
8097                        + mCurrentPackage.versionCode + "]");
8098
8099                // Reset per-package preconditions and fire the appropriate next state
8100                mWidgetData = null;
8101                final int type = mRestoreDescription.getDataType();
8102                if (type == RestoreDescription.TYPE_KEY_VALUE) {
8103                    nextState = UnifiedRestoreState.RESTORE_KEYVALUE;
8104                } else if (type == RestoreDescription.TYPE_FULL_STREAM) {
8105                    nextState = UnifiedRestoreState.RESTORE_FULL;
8106                } else {
8107                    // Unknown restore type; ignore this package and move on
8108                    Slog.e(TAG, "Unrecognized restore type " + type);
8109                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8110                    return;
8111                }
8112            } catch (RemoteException e) {
8113                Slog.e(TAG, "Can't get next target from transport; ending restore");
8114                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8115                nextState = UnifiedRestoreState.FINAL;
8116                return;
8117            } finally {
8118                executeNextState(nextState);
8119            }
8120        }
8121
8122        // state RESTORE_KEYVALUE : restore one package via key/value API set
8123        private void restoreKeyValue() {
8124            // Initiating the restore will pass responsibility for the state machine's
8125            // progress to the agent callback, so we do not always execute the
8126            // next state here.
8127            final String packageName = mCurrentPackage.packageName;
8128            // Validate some semantic requirements that apply in this way
8129            // only to the key/value restore API flow
8130            if (mCurrentPackage.applicationInfo.backupAgentName == null
8131                    || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
8132                if (MORE_DEBUG) {
8133                    Slog.i(TAG, "Data exists for package " + packageName
8134                            + " but app has no agent; skipping");
8135                }
8136                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8137                        "Package has no agent");
8138                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8139                return;
8140            }
8141
8142            Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
8143            if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
8144                Slog.w(TAG, "Signature mismatch restoring " + packageName);
8145                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8146                        "Signature mismatch");
8147                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8148                return;
8149            }
8150
8151            // Good to go!  Set up and bind the agent...
8152            mAgent = bindToAgentSynchronous(
8153                    mCurrentPackage.applicationInfo,
8154                    IApplicationThread.BACKUP_MODE_INCREMENTAL);
8155            if (mAgent == null) {
8156                Slog.w(TAG, "Can't find backup agent for " + packageName);
8157                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8158                        "Restore agent missing");
8159                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8160                return;
8161            }
8162
8163            // Whatever happens next, we've launched the target app now; remember that.
8164            mDidLaunch = true;
8165
8166            // And then finally start the restore on this agent
8167            try {
8168                initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
8169                ++mCount;
8170            } catch (Exception e) {
8171                Slog.e(TAG, "Error when attempting restore: " + e.toString());
8172                keyValueAgentErrorCleanup();
8173                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8174            }
8175        }
8176
8177        // Guts of a key/value restore operation
8178        void initiateOneRestore(PackageInfo app, int appVersionCode) {
8179            final String packageName = app.packageName;
8180
8181            if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
8182
8183            // !!! TODO: get the dirs from the transport
8184            mBackupDataName = new File(mDataDir, packageName + ".restore");
8185            mStageName = new File(mDataDir, packageName + ".stage");
8186            mNewStateName = new File(mStateDir, packageName + ".new");
8187            mSavedStateName = new File(mStateDir, packageName);
8188
8189            // don't stage the 'android' package where the wallpaper data lives.  this is
8190            // an optimization: we know there's no widget data hosted/published by that
8191            // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
8192            // data following the download.
8193            boolean staging = !packageName.equals("android");
8194            ParcelFileDescriptor stage;
8195            File downloadFile = (staging) ? mStageName : mBackupDataName;
8196
8197            final int token = generateToken();
8198            try {
8199                // Run the transport's restore pass
8200                stage = ParcelFileDescriptor.open(downloadFile,
8201                        ParcelFileDescriptor.MODE_READ_WRITE |
8202                        ParcelFileDescriptor.MODE_CREATE |
8203                        ParcelFileDescriptor.MODE_TRUNCATE);
8204
8205                if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
8206                    // Transport-level failure, so we wind everything up and
8207                    // terminate the restore operation.
8208                    Slog.e(TAG, "Error getting restore data for " + packageName);
8209                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8210                    stage.close();
8211                    downloadFile.delete();
8212                    executeNextState(UnifiedRestoreState.FINAL);
8213                    return;
8214                }
8215
8216                // We have the data from the transport. Now we extract and strip
8217                // any per-package metadata (typically widget-related information)
8218                // if appropriate
8219                if (staging) {
8220                    stage.close();
8221                    stage = ParcelFileDescriptor.open(downloadFile,
8222                            ParcelFileDescriptor.MODE_READ_ONLY);
8223
8224                    mBackupData = ParcelFileDescriptor.open(mBackupDataName,
8225                            ParcelFileDescriptor.MODE_READ_WRITE |
8226                            ParcelFileDescriptor.MODE_CREATE |
8227                            ParcelFileDescriptor.MODE_TRUNCATE);
8228
8229                    BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
8230                    BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
8231                    byte[] buffer = new byte[8192]; // will grow when needed
8232                    while (in.readNextHeader()) {
8233                        final String key = in.getKey();
8234                        final int size = in.getDataSize();
8235
8236                        // is this a special key?
8237                        if (key.equals(KEY_WIDGET_STATE)) {
8238                            if (DEBUG) {
8239                                Slog.i(TAG, "Restoring widget state for " + packageName);
8240                            }
8241                            mWidgetData = new byte[size];
8242                            in.readEntityData(mWidgetData, 0, size);
8243                        } else {
8244                            if (size > buffer.length) {
8245                                buffer = new byte[size];
8246                            }
8247                            in.readEntityData(buffer, 0, size);
8248                            out.writeEntityHeader(key, size);
8249                            out.writeEntityData(buffer, size);
8250                        }
8251                    }
8252
8253                    mBackupData.close();
8254                }
8255
8256                // Okay, we have the data.  Now have the agent do the restore.
8257                stage.close();
8258
8259                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
8260                        ParcelFileDescriptor.MODE_READ_ONLY);
8261
8262                mNewState = ParcelFileDescriptor.open(mNewStateName,
8263                        ParcelFileDescriptor.MODE_READ_WRITE |
8264                        ParcelFileDescriptor.MODE_CREATE |
8265                        ParcelFileDescriptor.MODE_TRUNCATE);
8266
8267                // Kick off the restore, checking for hung agents.  The timeout or
8268                // the operationComplete() callback will schedule the next step,
8269                // so we do not do that here.
8270                prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
8271                mAgent.doRestore(mBackupData, appVersionCode, mNewState,
8272                        token, mBackupManagerBinder);
8273            } catch (Exception e) {
8274                Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
8275                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8276                        packageName, e.toString());
8277                keyValueAgentErrorCleanup();    // clears any pending timeout messages as well
8278
8279                // After a restore failure we go back to running the queue.  If there
8280                // are no more packages to be restored that will be handled by the
8281                // next step.
8282                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8283            }
8284        }
8285
8286        // state RESTORE_FULL : restore one package via streaming engine
8287        private void restoreFull() {
8288            // None of this can run on the work looper here, so we spin asynchronous
8289            // work like this:
8290            //
8291            //   StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk()
8292            //                       write it into the pipe to the engine
8293            //   EngineThread: FullRestoreEngine thread communicating with the target app
8294            //
8295            // When finished, StreamFeederThread executes next state as appropriate on the
8296            // backup looper, and the overall unified restore task resumes
8297            try {
8298                StreamFeederThread feeder = new StreamFeederThread();
8299                if (MORE_DEBUG) {
8300                    Slog.i(TAG, "Spinning threads for stream restore of "
8301                            + mCurrentPackage.packageName);
8302                }
8303                new Thread(feeder, "unified-stream-feeder").start();
8304
8305                // At this point the feeder is responsible for advancing the restore
8306                // state, so we're done here.
8307            } catch (IOException e) {
8308                // Unable to instantiate the feeder thread -- we need to bail on the
8309                // current target.  We haven't asked the transport for data yet, though,
8310                // so we can do that simply by going back to running the restore queue.
8311                Slog.e(TAG, "Unable to construct pipes for stream restore!");
8312                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8313            }
8314        }
8315
8316        // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
8317        private void restoreFinished() {
8318            try {
8319                final int token = generateToken();
8320                prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
8321                mAgent.doRestoreFinished(token, mBackupManagerBinder);
8322                // If we get this far, the callback or timeout will schedule the
8323                // next restore state, so we're done
8324            } catch (Exception e) {
8325                final String packageName = mCurrentPackage.packageName;
8326                Slog.e(TAG, "Unable to finalize restore of " + packageName);
8327                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8328                        packageName, e.toString());
8329                keyValueAgentErrorCleanup();
8330                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8331            }
8332        }
8333
8334        class StreamFeederThread extends RestoreEngine implements Runnable {
8335            final String TAG = "StreamFeederThread";
8336            FullRestoreEngine mEngine;
8337
8338            // pipe through which we read data from the transport. [0] read, [1] write
8339            ParcelFileDescriptor[] mTransportPipes;
8340
8341            // pipe through which the engine will read data.  [0] read, [1] write
8342            ParcelFileDescriptor[] mEnginePipes;
8343
8344            public StreamFeederThread() throws IOException {
8345                mTransportPipes = ParcelFileDescriptor.createPipe();
8346                mEnginePipes = ParcelFileDescriptor.createPipe();
8347                setRunning(true);
8348            }
8349
8350            @Override
8351            public void run() {
8352                UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
8353                int status = BackupTransport.TRANSPORT_OK;
8354
8355                EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
8356                        mCurrentPackage.packageName);
8357
8358                mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false);
8359                EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]);
8360
8361                ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
8362                ParcelFileDescriptor tReadEnd = mTransportPipes[0];
8363                ParcelFileDescriptor tWriteEnd = mTransportPipes[1];
8364
8365                int bufferSize = 32 * 1024;
8366                byte[] buffer = new byte[bufferSize];
8367                FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor());
8368                FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor());
8369
8370                // spin up the engine and start moving data to it
8371                new Thread(eThread, "unified-restore-engine").start();
8372
8373                try {
8374                    while (status == BackupTransport.TRANSPORT_OK) {
8375                        // have the transport write some of the restoring data to us
8376                        int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd);
8377                        if (result > 0) {
8378                            // The transport wrote this many bytes of restore data to the
8379                            // pipe, so pass it along to the engine.
8380                            if (MORE_DEBUG) {
8381                                Slog.v(TAG, "  <- transport provided chunk size " + result);
8382                            }
8383                            if (result > bufferSize) {
8384                                bufferSize = result;
8385                                buffer = new byte[bufferSize];
8386                            }
8387                            int toCopy = result;
8388                            while (toCopy > 0) {
8389                                int n = transportIn.read(buffer, 0, toCopy);
8390                                engineOut.write(buffer, 0, n);
8391                                toCopy -= n;
8392                                if (MORE_DEBUG) {
8393                                    Slog.v(TAG, "  -> wrote " + n + " to engine, left=" + toCopy);
8394                                }
8395                            }
8396                        } else if (result == BackupTransport.NO_MORE_DATA) {
8397                            // Clean finish.  Wind up and we're done!
8398                            if (MORE_DEBUG) {
8399                                Slog.i(TAG, "Got clean full-restore EOF for "
8400                                        + mCurrentPackage.packageName);
8401                            }
8402                            status = BackupTransport.TRANSPORT_OK;
8403                            break;
8404                        } else {
8405                            // Transport reported some sort of failure; the fall-through
8406                            // handling will deal properly with that.
8407                            Slog.e(TAG, "Error " + result + " streaming restore for "
8408                                    + mCurrentPackage.packageName);
8409                            EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8410                            status = result;
8411                        }
8412                    }
8413                    if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through");
8414                } catch (IOException e) {
8415                    // We lost our ability to communicate via the pipes.  That's worrying
8416                    // but potentially recoverable; abandon this package's restore but
8417                    // carry on with the next restore target.
8418                    Slog.e(TAG, "Unable to route data for restore");
8419                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8420                            mCurrentPackage.packageName, "I/O error on pipes");
8421                    status = BackupTransport.AGENT_ERROR;
8422                } catch (RemoteException e) {
8423                    // The transport went away; terminate the whole operation.  Closing
8424                    // the sockets will wake up the engine and it will then tidy up the
8425                    // remote end.
8426                    Slog.e(TAG, "Transport failed during restore");
8427                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8428                    status = BackupTransport.TRANSPORT_ERROR;
8429                } finally {
8430                    // Close the transport pipes and *our* end of the engine pipe,
8431                    // but leave the engine thread's end open so that it properly
8432                    // hits EOF and winds up its operations.
8433                    IoUtils.closeQuietly(mEnginePipes[1]);
8434                    IoUtils.closeQuietly(mTransportPipes[0]);
8435                    IoUtils.closeQuietly(mTransportPipes[1]);
8436
8437                    // Don't proceed until the engine has finished
8438                    eThread.waitForResult();
8439
8440                    if (MORE_DEBUG) {
8441                        Slog.i(TAG, "engine thread finished; proceeding");
8442                    }
8443
8444                    // Now we're really done with this one too
8445                    IoUtils.closeQuietly(mEnginePipes[0]);
8446
8447                    // In all cases we want to remember whether we launched
8448                    // the target app as part of our work so far.
8449                    mDidLaunch = (mEngine.getAgent() != null);
8450
8451                    // If we hit a transport-level error, we are done with everything;
8452                    // if we hit an agent error we just go back to running the queue.
8453                    if (status == BackupTransport.TRANSPORT_OK) {
8454                        // Clean finish means we issue the restore-finished callback
8455                        nextState = UnifiedRestoreState.RESTORE_FINISHED;
8456
8457                        // the engine bound the target's agent, so recover that binding
8458                        // to use for the callback.
8459                        mAgent = mEngine.getAgent();
8460                    } else {
8461                        // Something went wrong somewhere.  Whether it was at the transport
8462                        // level is immaterial; we need to tell the transport to bail
8463                        try {
8464                            mTransport.abortFullRestore();
8465                        } catch (RemoteException e) {
8466                            // transport itself is dead; make sure we handle this as a
8467                            // fatal error
8468                            status = BackupTransport.TRANSPORT_ERROR;
8469                        }
8470
8471                        // We also need to wipe the current target's data, as it's probably
8472                        // in an incoherent state.
8473                        clearApplicationDataSynchronous(mCurrentPackage.packageName);
8474
8475                        // Schedule the next state based on the nature of our failure
8476                        if (status == BackupTransport.TRANSPORT_ERROR) {
8477                            nextState = UnifiedRestoreState.FINAL;
8478                        } else {
8479                            nextState = UnifiedRestoreState.RUNNING_QUEUE;
8480                        }
8481                    }
8482                    executeNextState(nextState);
8483                    setRunning(false);
8484                }
8485            }
8486
8487        }
8488
8489        class EngineThread implements Runnable {
8490            FullRestoreEngine mEngine;
8491            FileInputStream mEngineStream;
8492
8493            EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
8494                mEngine = engine;
8495                engine.setRunning(true);
8496                mEngineStream = new FileInputStream(engineSocket.getFileDescriptor());
8497            }
8498
8499            public boolean isRunning() {
8500                return mEngine.isRunning();
8501            }
8502
8503            public int waitForResult() {
8504                return mEngine.waitForResult();
8505            }
8506
8507            @Override
8508            public void run() {
8509                while (mEngine.isRunning()) {
8510                    // Tell it to be sure to leave the agent instance up after finishing
8511                    mEngine.restoreOneFile(mEngineStream, false);
8512                }
8513            }
8514        }
8515
8516        // state FINAL : tear everything down and we're done.
8517        private void finalizeRestore() {
8518            if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
8519
8520            try {
8521                mTransport.finishRestore();
8522            } catch (Exception e) {
8523                Slog.e(TAG, "Error finishing restore", e);
8524            }
8525
8526            // Tell the observer we're done
8527            if (mObserver != null) {
8528                try {
8529                    mObserver.restoreFinished(mStatus);
8530                } catch (RemoteException e) {
8531                    Slog.d(TAG, "Restore observer died at restoreFinished");
8532                }
8533            }
8534
8535            // Clear any ongoing session timeout.
8536            mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
8537
8538            // If we have a PM token, we must under all circumstances be sure to
8539            // handshake when we've finished.
8540            if (mPmToken > 0) {
8541                if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
8542                try {
8543                    mPackageManagerBinder.finishPackageInstall(mPmToken, mDidLaunch);
8544                } catch (RemoteException e) { /* can't happen */ }
8545            } else {
8546                // We were invoked via an active restore session, not by the Package
8547                // Manager, so start up the session timeout again.
8548                mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
8549                        TIMEOUT_RESTORE_INTERVAL);
8550            }
8551
8552            // Kick off any work that may be needed regarding app widget restores
8553            // TODO: http://b/22388012
8554            AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM);
8555
8556            // If this was a full-system restore, record the ancestral
8557            // dataset information
8558            if (mIsSystemRestore && mPmAgent != null) {
8559                mAncestralPackages = mPmAgent.getRestoredPackages();
8560                mAncestralToken = mToken;
8561                writeRestoreTokens();
8562            }
8563
8564            // done; we can finally release the wakelock and be legitimately done.
8565            Slog.i(TAG, "Restore complete.");
8566            mWakelock.release();
8567        }
8568
8569        void keyValueAgentErrorCleanup() {
8570            // If the agent fails restore, it might have put the app's data
8571            // into an incoherent state.  For consistency we wipe its data
8572            // again in this case before continuing with normal teardown
8573            clearApplicationDataSynchronous(mCurrentPackage.packageName);
8574            keyValueAgentCleanup();
8575        }
8576
8577        // TODO: clean up naming; this is now used at finish by both k/v and stream restores
8578        void keyValueAgentCleanup() {
8579            mBackupDataName.delete();
8580            mStageName.delete();
8581            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
8582            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
8583            mBackupData = mNewState = null;
8584
8585            // if everything went okay, remember the recorded state now
8586            //
8587            // !!! TODO: the restored data could be migrated on the server
8588            // side into the current dataset.  In that case the new state file
8589            // we just created would reflect the data already extant in the
8590            // backend, so there'd be nothing more to do.  Until that happens,
8591            // however, we need to make sure that we record the data to the
8592            // current backend dataset.  (Yes, this means shipping the data over
8593            // the wire in both directions.  That's bad, but consistency comes
8594            // first, then efficiency.)  Once we introduce server-side data
8595            // migration to the newly-restored device's dataset, we will change
8596            // the following from a discard of the newly-written state to the
8597            // "correct" operation of renaming into the canonical state blob.
8598            mNewStateName.delete();                      // TODO: remove; see above comment
8599            //mNewStateName.renameTo(mSavedStateName);   // TODO: replace with this
8600
8601            // If this wasn't the PM pseudopackage, tear down the agent side
8602            if (mCurrentPackage.applicationInfo != null) {
8603                // unbind and tidy up even on timeout or failure
8604                try {
8605                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
8606
8607                    // The agent was probably running with a stub Application object,
8608                    // which isn't a valid run mode for the main app logic.  Shut
8609                    // down the app so that next time it's launched, it gets the
8610                    // usual full initialization.  Note that this is only done for
8611                    // full-system restores: when a single app has requested a restore,
8612                    // it is explicitly not killed following that operation.
8613                    //
8614                    // We execute this kill when these conditions hold:
8615                    //    1. it's not a system-uid process,
8616                    //    2. the app did not request its own restore (mTargetPackage == null), and either
8617                    //    3a. the app is a full-data target (TYPE_FULL_STREAM) or
8618                    //     b. the app does not state android:killAfterRestore="false" in its manifest
8619                    final int appFlags = mCurrentPackage.applicationInfo.flags;
8620                    final boolean killAfterRestore =
8621                            (mCurrentPackage.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
8622                            && ((mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM)
8623                                    || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0));
8624
8625                    if (mTargetPackage == null && killAfterRestore) {
8626                        if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
8627                                + mCurrentPackage.applicationInfo.processName);
8628                        mActivityManager.killApplicationProcess(
8629                                mCurrentPackage.applicationInfo.processName,
8630                                mCurrentPackage.applicationInfo.uid);
8631                    }
8632                } catch (RemoteException e) {
8633                    // can't happen; we run in the same process as the activity manager
8634                }
8635            }
8636
8637            // The caller is responsible for reestablishing the state machine; our
8638            // responsibility here is to clear the decks for whatever comes next.
8639            mBackupHandler.removeMessages(MSG_TIMEOUT, this);
8640            synchronized (mCurrentOpLock) {
8641                mCurrentOperations.clear();
8642            }
8643        }
8644
8645        @Override
8646        public void operationComplete(long unusedResult) {
8647            if (MORE_DEBUG) {
8648                Slog.i(TAG, "operationComplete() during restore: target="
8649                        + mCurrentPackage.packageName
8650                        + " state=" + mState);
8651            }
8652
8653            final UnifiedRestoreState nextState;
8654            switch (mState) {
8655                case INITIAL:
8656                    // We've just (manually) restored the PMBA.  It doesn't need the
8657                    // additional restore-finished callback so we bypass that and go
8658                    // directly to running the queue.
8659                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8660                    break;
8661
8662                case RESTORE_KEYVALUE:
8663                case RESTORE_FULL: {
8664                    // Okay, we've just heard back from the agent that it's done with
8665                    // the restore itself.  We now have to send the same agent its
8666                    // doRestoreFinished() callback, so roll into that state.
8667                    nextState = UnifiedRestoreState.RESTORE_FINISHED;
8668                    break;
8669                }
8670
8671                case RESTORE_FINISHED: {
8672                    // Okay, we're done with this package.  Tidy up and go on to the next
8673                    // app in the queue.
8674                    int size = (int) mBackupDataName.length();
8675                    EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
8676                            mCurrentPackage.packageName, size);
8677
8678                    // Just go back to running the restore queue
8679                    keyValueAgentCleanup();
8680
8681                    // If there was widget state associated with this app, get the OS to
8682                    // incorporate it into current bookeeping and then pass that along to
8683                    // the app as part of the restore-time work.
8684                    if (mWidgetData != null) {
8685                        restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
8686                    }
8687
8688                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8689                    break;
8690                }
8691
8692                default: {
8693                    // Some kind of horrible semantic error; we're in an unexpected state.
8694                    // Back off hard and wind up.
8695                    Slog.e(TAG, "Unexpected restore callback into state " + mState);
8696                    keyValueAgentErrorCleanup();
8697                    nextState = UnifiedRestoreState.FINAL;
8698                    break;
8699                }
8700            }
8701
8702            executeNextState(nextState);
8703        }
8704
8705        // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
8706        @Override
8707        public void handleTimeout() {
8708            Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
8709            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8710                    mCurrentPackage.packageName, "restore timeout");
8711            // Handle like an agent that threw on invocation: wipe it and go on to the next
8712            keyValueAgentErrorCleanup();
8713            executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8714        }
8715
8716        void executeNextState(UnifiedRestoreState nextState) {
8717            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
8718                    + this + " nextState=" + nextState);
8719            mState = nextState;
8720            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
8721            mBackupHandler.sendMessage(msg);
8722        }
8723
8724        // restore observer support
8725        void sendStartRestore(int numPackages) {
8726            if (mObserver != null) {
8727                try {
8728                    mObserver.restoreStarting(numPackages);
8729                } catch (RemoteException e) {
8730                    Slog.w(TAG, "Restore observer went away: startRestore");
8731                    mObserver = null;
8732                }
8733            }
8734        }
8735
8736        void sendOnRestorePackage(String name) {
8737            if (mObserver != null) {
8738                if (mObserver != null) {
8739                    try {
8740                        mObserver.onUpdate(mCount, name);
8741                    } catch (RemoteException e) {
8742                        Slog.d(TAG, "Restore observer died in onUpdate");
8743                        mObserver = null;
8744                    }
8745                }
8746            }
8747        }
8748
8749        void sendEndRestore() {
8750            if (mObserver != null) {
8751                try {
8752                    mObserver.restoreFinished(mStatus);
8753                } catch (RemoteException e) {
8754                    Slog.w(TAG, "Restore observer went away: endRestore");
8755                    mObserver = null;
8756                }
8757            }
8758        }
8759    }
8760
8761    class PerformClearTask implements Runnable {
8762        IBackupTransport mTransport;
8763        PackageInfo mPackage;
8764
8765        PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
8766            mTransport = transport;
8767            mPackage = packageInfo;
8768        }
8769
8770        public void run() {
8771            try {
8772                // Clear the on-device backup state to ensure a full backup next time
8773                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
8774                File stateFile = new File(stateDir, mPackage.packageName);
8775                stateFile.delete();
8776
8777                // Tell the transport to remove all the persistent storage for the app
8778                // TODO - need to handle failures
8779                mTransport.clearBackupData(mPackage);
8780            } catch (RemoteException e) {
8781                // can't happen; the transport is local
8782            } catch (Exception e) {
8783                Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
8784            } finally {
8785                try {
8786                    // TODO - need to handle failures
8787                    mTransport.finishBackup();
8788                } catch (RemoteException e) {
8789                    // can't happen; the transport is local
8790                }
8791
8792                // Last but not least, release the cpu
8793                mWakelock.release();
8794            }
8795        }
8796    }
8797
8798    class PerformInitializeTask implements Runnable {
8799        HashSet<String> mQueue;
8800
8801        PerformInitializeTask(HashSet<String> transportNames) {
8802            mQueue = transportNames;
8803        }
8804
8805        public void run() {
8806            try {
8807                for (String transportName : mQueue) {
8808                    IBackupTransport transport = getTransport(transportName);
8809                    if (transport == null) {
8810                        Slog.e(TAG, "Requested init for " + transportName + " but not found");
8811                        continue;
8812                    }
8813
8814                    Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
8815                    EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
8816                    long startRealtime = SystemClock.elapsedRealtime();
8817                    int status = transport.initializeDevice();
8818
8819                    if (status == BackupTransport.TRANSPORT_OK) {
8820                        status = transport.finishBackup();
8821                    }
8822
8823                    // Okay, the wipe really happened.  Clean up our local bookkeeping.
8824                    if (status == BackupTransport.TRANSPORT_OK) {
8825                        Slog.i(TAG, "Device init successful");
8826                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
8827                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
8828                        resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
8829                        EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
8830                        synchronized (mQueueLock) {
8831                            recordInitPendingLocked(false, transportName);
8832                        }
8833                    } else {
8834                        // If this didn't work, requeue this one and try again
8835                        // after a suitable interval
8836                        Slog.e(TAG, "Transport error in initializeDevice()");
8837                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
8838                        synchronized (mQueueLock) {
8839                            recordInitPendingLocked(true, transportName);
8840                        }
8841                        // do this via another alarm to make sure of the wakelock states
8842                        long delay = transport.requestBackupTime();
8843                        Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay);
8844                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
8845                                System.currentTimeMillis() + delay, mRunInitIntent);
8846                    }
8847                }
8848            } catch (RemoteException e) {
8849                // can't happen; the transports are local
8850            } catch (Exception e) {
8851                Slog.e(TAG, "Unexpected error performing init", e);
8852            } finally {
8853                // Done; release the wakelock
8854                mWakelock.release();
8855            }
8856        }
8857    }
8858
8859    private void dataChangedImpl(String packageName) {
8860        HashSet<String> targets = dataChangedTargets(packageName);
8861        dataChangedImpl(packageName, targets);
8862    }
8863
8864    private void dataChangedImpl(String packageName, HashSet<String> targets) {
8865        // Record that we need a backup pass for the caller.  Since multiple callers
8866        // may share a uid, we need to note all candidates within that uid and schedule
8867        // a backup pass for each of them.
8868        if (targets == null) {
8869            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8870                   + " uid=" + Binder.getCallingUid());
8871            return;
8872        }
8873
8874        synchronized (mQueueLock) {
8875            // Note that this client has made data changes that need to be backed up
8876            if (targets.contains(packageName)) {
8877                // Add the caller to the set of pending backups.  If there is
8878                // one already there, then overwrite it, but no harm done.
8879                BackupRequest req = new BackupRequest(packageName);
8880                if (mPendingBackups.put(packageName, req) == null) {
8881                    if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
8882
8883                    // Journal this request in case of crash.  The put()
8884                    // operation returned null when this package was not already
8885                    // in the set; we want to avoid touching the disk redundantly.
8886                    writeToJournalLocked(packageName);
8887                }
8888            }
8889        }
8890
8891        // ...and schedule a backup pass if necessary
8892        KeyValueBackupJob.schedule(mContext);
8893    }
8894
8895    // Note: packageName is currently unused, but may be in the future
8896    private HashSet<String> dataChangedTargets(String packageName) {
8897        // If the caller does not hold the BACKUP permission, it can only request a
8898        // backup of its own data.
8899        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8900                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8901            synchronized (mBackupParticipants) {
8902                return mBackupParticipants.get(Binder.getCallingUid());
8903            }
8904        }
8905
8906        // a caller with full permission can ask to back up any participating app
8907        HashSet<String> targets = new HashSet<String>();
8908        if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
8909            targets.add(PACKAGE_MANAGER_SENTINEL);
8910        } else {
8911            synchronized (mBackupParticipants) {
8912                int N = mBackupParticipants.size();
8913                for (int i = 0; i < N; i++) {
8914                    HashSet<String> s = mBackupParticipants.valueAt(i);
8915                    if (s != null) {
8916                        targets.addAll(s);
8917                    }
8918                }
8919            }
8920        }
8921        return targets;
8922    }
8923
8924    private void writeToJournalLocked(String str) {
8925        RandomAccessFile out = null;
8926        try {
8927            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
8928            out = new RandomAccessFile(mJournal, "rws");
8929            out.seek(out.length());
8930            out.writeUTF(str);
8931        } catch (IOException e) {
8932            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
8933            mJournal = null;
8934        } finally {
8935            try { if (out != null) out.close(); } catch (IOException e) {}
8936        }
8937    }
8938
8939    // ----- IBackupManager binder interface -----
8940
8941    public void dataChanged(final String packageName) {
8942        final int callingUserHandle = UserHandle.getCallingUserId();
8943        if (callingUserHandle != UserHandle.USER_SYSTEM) {
8944            // TODO: http://b/22388012
8945            // App is running under a non-owner user profile.  For now, we do not back
8946            // up data from secondary user profiles.
8947            // TODO: backups for all user profiles although don't add backup for profiles
8948            // without adding admin control in DevicePolicyManager.
8949            if (MORE_DEBUG) {
8950                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
8951                        + callingUserHandle);
8952            }
8953            return;
8954        }
8955
8956        final HashSet<String> targets = dataChangedTargets(packageName);
8957        if (targets == null) {
8958            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8959                   + " uid=" + Binder.getCallingUid());
8960            return;
8961        }
8962
8963        mBackupHandler.post(new Runnable() {
8964                public void run() {
8965                    dataChangedImpl(packageName, targets);
8966                }
8967            });
8968    }
8969
8970    // Clear the given package's backup data from the current transport
8971    public void clearBackupData(String transportName, String packageName) {
8972        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
8973        PackageInfo info;
8974        try {
8975            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
8976        } catch (NameNotFoundException e) {
8977            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
8978            return;
8979        }
8980
8981        // If the caller does not hold the BACKUP permission, it can only request a
8982        // wipe of its own backed-up data.
8983        HashSet<String> apps;
8984        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8985                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8986            apps = mBackupParticipants.get(Binder.getCallingUid());
8987        } else {
8988            // a caller with full permission can ask to back up any participating app
8989            // !!! TODO: allow data-clear of ANY app?
8990            if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
8991            apps = new HashSet<String>();
8992            int N = mBackupParticipants.size();
8993            for (int i = 0; i < N; i++) {
8994                HashSet<String> s = mBackupParticipants.valueAt(i);
8995                if (s != null) {
8996                    apps.addAll(s);
8997                }
8998            }
8999        }
9000
9001        // Is the given app an available participant?
9002        if (apps.contains(packageName)) {
9003            // found it; fire off the clear request
9004            if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
9005            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
9006            synchronized (mQueueLock) {
9007                final IBackupTransport transport = getTransport(transportName);
9008                if (transport == null) {
9009                    // transport is currently unavailable -- make sure to retry
9010                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
9011                            new ClearRetryParams(transportName, packageName));
9012                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
9013                    return;
9014                }
9015                long oldId = Binder.clearCallingIdentity();
9016                mWakelock.acquire();
9017                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
9018                        new ClearParams(transport, info));
9019                mBackupHandler.sendMessage(msg);
9020                Binder.restoreCallingIdentity(oldId);
9021            }
9022        }
9023    }
9024
9025    // Run a backup pass immediately for any applications that have declared
9026    // that they have pending updates.
9027    public void backupNow() {
9028        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
9029
9030        if (mPowerManager.isPowerSaveMode()) {
9031            if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
9032            KeyValueBackupJob.schedule(mContext);   // try again in several hours
9033        } else {
9034            if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
9035            synchronized (mQueueLock) {
9036                // Fire the intent that kicks off the whole shebang...
9037                try {
9038                    mRunBackupIntent.send();
9039                } catch (PendingIntent.CanceledException e) {
9040                    // should never happen
9041                    Slog.e(TAG, "run-backup intent cancelled!");
9042                }
9043
9044                // ...and cancel any pending scheduled job, because we've just superseded it
9045                KeyValueBackupJob.cancel(mContext);
9046            }
9047        }
9048    }
9049
9050    boolean deviceIsProvisioned() {
9051        final ContentResolver resolver = mContext.getContentResolver();
9052        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
9053    }
9054
9055    // Run a *full* backup pass for the given packages, writing the resulting data stream
9056    // to the supplied file descriptor.  This method is synchronous and does not return
9057    // to the caller until the backup has been completed.
9058    //
9059    // This is the variant used by 'adb backup'; it requires on-screen confirmation
9060    // by the user because it can be used to offload data over untrusted USB.
9061    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
9062            boolean includeObbs, boolean includeShared, boolean doWidgets,
9063            boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) {
9064        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
9065
9066        final int callingUserHandle = UserHandle.getCallingUserId();
9067        // TODO: http://b/22388012
9068        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9069            throw new IllegalStateException("Backup supported only for the device owner");
9070        }
9071
9072        // Validate
9073        if (!doAllApps) {
9074            if (!includeShared) {
9075                // If we're backing up shared data (sdcard or equivalent), then we can run
9076                // without any supplied app names.  Otherwise, we'd be doing no work, so
9077                // report the error.
9078                if (pkgList == null || pkgList.length == 0) {
9079                    throw new IllegalArgumentException(
9080                            "Backup requested but neither shared nor any apps named");
9081                }
9082            }
9083        }
9084
9085        long oldId = Binder.clearCallingIdentity();
9086        try {
9087            // Doesn't make sense to do a full backup prior to setup
9088            if (!deviceIsProvisioned()) {
9089                Slog.i(TAG, "Full backup not supported before setup");
9090                return;
9091            }
9092
9093            if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
9094                    + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
9095                    + " system=" + includeSystem + " pkgs=" + pkgList);
9096            Slog.i(TAG, "Beginning full backup...");
9097
9098            FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
9099                    includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList);
9100            final int token = generateToken();
9101            synchronized (mFullConfirmations) {
9102                mFullConfirmations.put(token, params);
9103            }
9104
9105            // start up the confirmation UI
9106            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
9107            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
9108                Slog.e(TAG, "Unable to launch full backup confirmation");
9109                mFullConfirmations.delete(token);
9110                return;
9111            }
9112
9113            // make sure the screen is lit for the user interaction
9114            mPowerManager.userActivity(SystemClock.uptimeMillis(),
9115                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
9116                    0);
9117
9118            // start the confirmation countdown
9119            startConfirmationTimeout(token, params);
9120
9121            // wait for the backup to be performed
9122            if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
9123            waitForCompletion(params);
9124        } finally {
9125            try {
9126                fd.close();
9127            } catch (IOException e) {
9128                // just eat it
9129            }
9130            Binder.restoreCallingIdentity(oldId);
9131            Slog.d(TAG, "Full backup processing complete.");
9132        }
9133    }
9134
9135    public void fullTransportBackup(String[] pkgNames) {
9136        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
9137                "fullTransportBackup");
9138
9139        final int callingUserHandle = UserHandle.getCallingUserId();
9140        // TODO: http://b/22388012
9141        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9142            throw new IllegalStateException("Restore supported only for the device owner");
9143        }
9144
9145        if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
9146            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
9147        } else {
9148            if (DEBUG) {
9149                Slog.d(TAG, "fullTransportBackup()");
9150            }
9151
9152            final long oldId = Binder.clearCallingIdentity();
9153            try {
9154                CountDownLatch latch = new CountDownLatch(1);
9155                PerformFullTransportBackupTask task = new PerformFullTransportBackupTask(null,
9156                        pkgNames, false, null, latch, null, false /* userInitiated */);
9157                // Acquiring wakelock for PerformFullTransportBackupTask before its start.
9158                mWakelock.acquire();
9159                (new Thread(task, "full-transport-master")).start();
9160                do {
9161                    try {
9162                        latch.await();
9163                        break;
9164                    } catch (InterruptedException e) {
9165                        // Just go back to waiting for the latch to indicate completion
9166                    }
9167                } while (true);
9168
9169                // We just ran a backup on these packages, so kick them to the end of the queue
9170                final long now = System.currentTimeMillis();
9171                for (String pkg : pkgNames) {
9172                    enqueueFullBackup(pkg, now);
9173                }
9174            } finally {
9175                Binder.restoreCallingIdentity(oldId);
9176            }
9177        }
9178
9179        if (DEBUG) {
9180            Slog.d(TAG, "Done with full transport backup.");
9181        }
9182    }
9183
9184    public void fullRestore(ParcelFileDescriptor fd) {
9185        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
9186
9187        final int callingUserHandle = UserHandle.getCallingUserId();
9188        // TODO: http://b/22388012
9189        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9190            throw new IllegalStateException("Restore supported only for the device owner");
9191        }
9192
9193        long oldId = Binder.clearCallingIdentity();
9194
9195        try {
9196            // Check whether the device has been provisioned -- we don't handle
9197            // full restores prior to completing the setup process.
9198            if (!deviceIsProvisioned()) {
9199                Slog.i(TAG, "Full restore not permitted before setup");
9200                return;
9201            }
9202
9203            Slog.i(TAG, "Beginning full restore...");
9204
9205            FullRestoreParams params = new FullRestoreParams(fd);
9206            final int token = generateToken();
9207            synchronized (mFullConfirmations) {
9208                mFullConfirmations.put(token, params);
9209            }
9210
9211            // start up the confirmation UI
9212            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
9213            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
9214                Slog.e(TAG, "Unable to launch full restore confirmation");
9215                mFullConfirmations.delete(token);
9216                return;
9217            }
9218
9219            // make sure the screen is lit for the user interaction
9220            mPowerManager.userActivity(SystemClock.uptimeMillis(),
9221                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
9222                    0);
9223
9224            // start the confirmation countdown
9225            startConfirmationTimeout(token, params);
9226
9227            // wait for the restore to be performed
9228            if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
9229            waitForCompletion(params);
9230        } finally {
9231            try {
9232                fd.close();
9233            } catch (IOException e) {
9234                Slog.w(TAG, "Error trying to close fd after full restore: " + e);
9235            }
9236            Binder.restoreCallingIdentity(oldId);
9237            Slog.i(TAG, "Full restore processing complete.");
9238        }
9239    }
9240
9241    boolean startConfirmationUi(int token, String action) {
9242        try {
9243            Intent confIntent = new Intent(action);
9244            confIntent.setClassName("com.android.backupconfirm",
9245                    "com.android.backupconfirm.BackupRestoreConfirmation");
9246            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
9247            confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
9248            mContext.startActivity(confIntent);
9249        } catch (ActivityNotFoundException e) {
9250            return false;
9251        }
9252        return true;
9253    }
9254
9255    void startConfirmationTimeout(int token, FullParams params) {
9256        if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
9257                + TIMEOUT_FULL_CONFIRMATION + " millis");
9258        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
9259                token, 0, params);
9260        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
9261    }
9262
9263    void waitForCompletion(FullParams params) {
9264        synchronized (params.latch) {
9265            while (params.latch.get() == false) {
9266                try {
9267                    params.latch.wait();
9268                } catch (InterruptedException e) { /* never interrupted */ }
9269            }
9270        }
9271    }
9272
9273    void signalFullBackupRestoreCompletion(FullParams params) {
9274        synchronized (params.latch) {
9275            params.latch.set(true);
9276            params.latch.notifyAll();
9277        }
9278    }
9279
9280    // Confirm that the previously-requested full backup/restore operation can proceed.  This
9281    // is used to require a user-facing disclosure about the operation.
9282    public void acknowledgeFullBackupOrRestore(int token, boolean allow,
9283            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
9284        if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
9285                + " allow=" + allow);
9286
9287        // TODO: possibly require not just this signature-only permission, but even
9288        // require that the specific designated confirmation-UI app uid is the caller?
9289        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
9290
9291        long oldId = Binder.clearCallingIdentity();
9292        try {
9293
9294            FullParams params;
9295            synchronized (mFullConfirmations) {
9296                params = mFullConfirmations.get(token);
9297                if (params != null) {
9298                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
9299                    mFullConfirmations.delete(token);
9300
9301                    if (allow) {
9302                        final int verb = params instanceof FullBackupParams
9303                                ? MSG_RUN_ADB_BACKUP
9304                                : MSG_RUN_ADB_RESTORE;
9305
9306                        params.observer = observer;
9307                        params.curPassword = curPassword;
9308
9309                        params.encryptPassword = encPpassword;
9310
9311                        if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
9312                        mWakelock.acquire();
9313                        Message msg = mBackupHandler.obtainMessage(verb, params);
9314                        mBackupHandler.sendMessage(msg);
9315                    } else {
9316                        Slog.w(TAG, "User rejected full backup/restore operation");
9317                        // indicate completion without having actually transferred any data
9318                        signalFullBackupRestoreCompletion(params);
9319                    }
9320                } else {
9321                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
9322                }
9323            }
9324        } finally {
9325            Binder.restoreCallingIdentity(oldId);
9326        }
9327    }
9328
9329    private static boolean backupSettingMigrated(int userId) {
9330        File base = new File(Environment.getDataDirectory(), "backup");
9331        File enableFile = new File(base, BACKUP_ENABLE_FILE);
9332        return enableFile.exists();
9333    }
9334
9335    private static boolean readBackupEnableState(int userId) {
9336        File base = new File(Environment.getDataDirectory(), "backup");
9337        File enableFile = new File(base, BACKUP_ENABLE_FILE);
9338        if (enableFile.exists()) {
9339            try (FileInputStream fin = new FileInputStream(enableFile)) {
9340                int state = fin.read();
9341                return state != 0;
9342            } catch (IOException e) {
9343                // can't read the file; fall through to assume disabled
9344                Slog.e(TAG, "Cannot read enable state; assuming disabled");
9345            }
9346        } else {
9347            if (DEBUG) {
9348                Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
9349            }
9350        }
9351        return false;
9352    }
9353
9354    private static void writeBackupEnableState(boolean enable, int userId) {
9355        File base = new File(Environment.getDataDirectory(), "backup");
9356        File enableFile = new File(base, BACKUP_ENABLE_FILE);
9357        File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
9358        FileOutputStream fout = null;
9359        try {
9360            fout = new FileOutputStream(stage);
9361            fout.write(enable ? 1 : 0);
9362            fout.close();
9363            stage.renameTo(enableFile);
9364            // will be synced immediately by the try-with-resources call to close()
9365        } catch (IOException|RuntimeException e) {
9366            // Whoops; looks like we're doomed.  Roll everything out, disabled,
9367            // including the legacy state.
9368            Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
9369                    + e.getMessage());
9370
9371            final ContentResolver r = sInstance.mContext.getContentResolver();
9372            Settings.Secure.putStringForUser(r,
9373                    Settings.Secure.BACKUP_ENABLED, null, userId);
9374            enableFile.delete();
9375            stage.delete();
9376        } finally {
9377            IoUtils.closeQuietly(fout);
9378        }
9379    }
9380
9381    // Enable/disable backups
9382    public void setBackupEnabled(boolean enable) {
9383        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9384                "setBackupEnabled");
9385
9386        Slog.i(TAG, "Backup enabled => " + enable);
9387
9388        long oldId = Binder.clearCallingIdentity();
9389        try {
9390            boolean wasEnabled = mEnabled;
9391            synchronized (this) {
9392                writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
9393                mEnabled = enable;
9394            }
9395
9396            synchronized (mQueueLock) {
9397                if (enable && !wasEnabled && mProvisioned) {
9398                    // if we've just been enabled, start scheduling backup passes
9399                    KeyValueBackupJob.schedule(mContext);
9400                    scheduleNextFullBackupJob(0);
9401                } else if (!enable) {
9402                    // No longer enabled, so stop running backups
9403                    if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
9404
9405                    KeyValueBackupJob.cancel(mContext);
9406
9407                    // This also constitutes an opt-out, so we wipe any data for
9408                    // this device from the backend.  We start that process with
9409                    // an alarm in order to guarantee wakelock states.
9410                    if (wasEnabled && mProvisioned) {
9411                        // NOTE: we currently flush every registered transport, not just
9412                        // the currently-active one.
9413                        HashSet<String> allTransports;
9414                        synchronized (mTransports) {
9415                            allTransports = new HashSet<String>(mTransports.keySet());
9416                        }
9417                        // build the set of transports for which we are posting an init
9418                        for (String transport : allTransports) {
9419                            recordInitPendingLocked(true, transport);
9420                        }
9421                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
9422                                mRunInitIntent);
9423                    }
9424                }
9425            }
9426        } finally {
9427            Binder.restoreCallingIdentity(oldId);
9428        }
9429    }
9430
9431    // Enable/disable automatic restore of app data at install time
9432    public void setAutoRestore(boolean doAutoRestore) {
9433        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9434                "setAutoRestore");
9435
9436        Slog.i(TAG, "Auto restore => " + doAutoRestore);
9437
9438        final long oldId = Binder.clearCallingIdentity();
9439        try {
9440            synchronized (this) {
9441                Settings.Secure.putInt(mContext.getContentResolver(),
9442                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
9443                mAutoRestore = doAutoRestore;
9444            }
9445        } finally {
9446            Binder.restoreCallingIdentity(oldId);
9447        }
9448    }
9449
9450    // Mark the backup service as having been provisioned
9451    public void setBackupProvisioned(boolean available) {
9452        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9453                "setBackupProvisioned");
9454        /*
9455         * This is now a no-op; provisioning is simply the device's own setup state.
9456         */
9457    }
9458
9459    // Report whether the backup mechanism is currently enabled
9460    public boolean isBackupEnabled() {
9461        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
9462        return mEnabled;    // no need to synchronize just to read it
9463    }
9464
9465    // Report the name of the currently active transport
9466    public String getCurrentTransport() {
9467        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9468                "getCurrentTransport");
9469        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
9470        return mCurrentTransport;
9471    }
9472
9473    // Report all known, available backup transports
9474    public String[] listAllTransports() {
9475        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
9476
9477        String[] list = null;
9478        ArrayList<String> known = new ArrayList<String>();
9479        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
9480            if (entry.getValue() != null) {
9481                known.add(entry.getKey());
9482            }
9483        }
9484
9485        if (known.size() > 0) {
9486            list = new String[known.size()];
9487            known.toArray(list);
9488        }
9489        return list;
9490    }
9491
9492    // Select which transport to use for the next backup operation.
9493    public String selectBackupTransport(String transport) {
9494        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9495                "selectBackupTransport");
9496
9497        synchronized (mTransports) {
9498            final long oldId = Binder.clearCallingIdentity();
9499            try {
9500                String prevTransport = mCurrentTransport;
9501                mCurrentTransport = transport;
9502                Settings.Secure.putString(mContext.getContentResolver(),
9503                        Settings.Secure.BACKUP_TRANSPORT, transport);
9504                Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
9505                        + " returning " + prevTransport);
9506                return prevTransport;
9507            } finally {
9508                Binder.restoreCallingIdentity(oldId);
9509            }
9510        }
9511    }
9512
9513    // Supply the configuration Intent for the given transport.  If the name is not one
9514    // of the available transports, or if the transport does not supply any configuration
9515    // UI, the method returns null.
9516    public Intent getConfigurationIntent(String transportName) {
9517        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9518                "getConfigurationIntent");
9519
9520        synchronized (mTransports) {
9521            final IBackupTransport transport = mTransports.get(transportName);
9522            if (transport != null) {
9523                try {
9524                    final Intent intent = transport.configurationIntent();
9525                    if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
9526                            + intent);
9527                    return intent;
9528                } catch (RemoteException e) {
9529                    /* fall through to return null */
9530                }
9531            }
9532        }
9533
9534        return null;
9535    }
9536
9537    // Supply the configuration summary string for the given transport.  If the name is
9538    // not one of the available transports, or if the transport does not supply any
9539    // summary / destination string, the method can return null.
9540    //
9541    // This string is used VERBATIM as the summary text of the relevant Settings item!
9542    public String getDestinationString(String transportName) {
9543        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9544                "getDestinationString");
9545
9546        synchronized (mTransports) {
9547            final IBackupTransport transport = mTransports.get(transportName);
9548            if (transport != null) {
9549                try {
9550                    final String text = transport.currentDestinationString();
9551                    if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
9552                    return text;
9553                } catch (RemoteException e) {
9554                    /* fall through to return null */
9555                }
9556            }
9557        }
9558
9559        return null;
9560    }
9561
9562    // Supply the manage-data intent for the given transport.
9563    public Intent getDataManagementIntent(String transportName) {
9564        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9565                "getDataManagementIntent");
9566
9567        synchronized (mTransports) {
9568            final IBackupTransport transport = mTransports.get(transportName);
9569            if (transport != null) {
9570                try {
9571                    final Intent intent = transport.dataManagementIntent();
9572                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
9573                            + intent);
9574                    return intent;
9575                } catch (RemoteException e) {
9576                    /* fall through to return null */
9577                }
9578            }
9579        }
9580
9581        return null;
9582    }
9583
9584    // Supply the menu label for affordances that fire the manage-data intent
9585    // for the given transport.
9586    public String getDataManagementLabel(String transportName) {
9587        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9588                "getDataManagementLabel");
9589
9590        synchronized (mTransports) {
9591            final IBackupTransport transport = mTransports.get(transportName);
9592            if (transport != null) {
9593                try {
9594                    final String text = transport.dataManagementLabel();
9595                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
9596                    return text;
9597                } catch (RemoteException e) {
9598                    /* fall through to return null */
9599                }
9600            }
9601        }
9602
9603        return null;
9604    }
9605
9606    // Callback: a requested backup agent has been instantiated.  This should only
9607    // be called from the Activity Manager.
9608    public void agentConnected(String packageName, IBinder agentBinder) {
9609        synchronized(mAgentConnectLock) {
9610            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
9611                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
9612                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
9613                mConnectedAgent = agent;
9614                mConnecting = false;
9615            } else {
9616                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9617                        + " claiming agent connected");
9618            }
9619            mAgentConnectLock.notifyAll();
9620        }
9621    }
9622
9623    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
9624    // If the agent failed to come up in the first place, the agentBinder argument
9625    // will be null.  This should only be called from the Activity Manager.
9626    public void agentDisconnected(String packageName) {
9627        // TODO: handle backup being interrupted
9628        synchronized(mAgentConnectLock) {
9629            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
9630                mConnectedAgent = null;
9631                mConnecting = false;
9632            } else {
9633                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9634                        + " claiming agent disconnected");
9635            }
9636            mAgentConnectLock.notifyAll();
9637        }
9638    }
9639
9640    // An application being installed will need a restore pass, then the Package Manager
9641    // will need to be told when the restore is finished.
9642    public void restoreAtInstall(String packageName, int token) {
9643        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
9644            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9645                    + " attemping install-time restore");
9646            return;
9647        }
9648
9649        boolean skip = false;
9650
9651        long restoreSet = getAvailableRestoreToken(packageName);
9652        if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
9653                + " token=" + Integer.toHexString(token)
9654                + " restoreSet=" + Long.toHexString(restoreSet));
9655        if (restoreSet == 0) {
9656            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
9657            skip = true;
9658        }
9659
9660        // Do we have a transport to fetch data for us?
9661        IBackupTransport transport = getTransport(mCurrentTransport);
9662        if (transport == null) {
9663            if (DEBUG) Slog.w(TAG, "No transport");
9664            skip = true;
9665        }
9666
9667        if (!mAutoRestore) {
9668            if (DEBUG) {
9669                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
9670            }
9671            skip = true;
9672        }
9673
9674        if (!skip) {
9675            try {
9676                // okay, we're going to attempt a restore of this package from this restore set.
9677                // The eventual message back into the Package Manager to run the post-install
9678                // steps for 'token' will be issued from the restore handling code.
9679
9680                // This can throw and so *must* happen before the wakelock is acquired
9681                String dirName = transport.transportDirName();
9682
9683                mWakelock.acquire();
9684                if (MORE_DEBUG) {
9685                    Slog.d(TAG, "Restore at install of " + packageName);
9686                }
9687                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9688                msg.obj = new RestoreParams(transport, dirName, null,
9689                        restoreSet, packageName, token);
9690                mBackupHandler.sendMessage(msg);
9691            } catch (RemoteException e) {
9692                // Binding to the transport broke; back off and proceed with the installation.
9693                Slog.e(TAG, "Unable to contact transport");
9694                skip = true;
9695            }
9696        }
9697
9698        if (skip) {
9699            // Auto-restore disabled or no way to attempt a restore; just tell the Package
9700            // Manager to proceed with the post-install handling for this package.
9701            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
9702            try {
9703                mPackageManagerBinder.finishPackageInstall(token, false);
9704            } catch (RemoteException e) { /* can't happen */ }
9705        }
9706    }
9707
9708    // Hand off a restore session
9709    public IRestoreSession beginRestoreSession(String packageName, String transport) {
9710        if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
9711                + " transport=" + transport);
9712
9713        boolean needPermission = true;
9714        if (transport == null) {
9715            transport = mCurrentTransport;
9716
9717            if (packageName != null) {
9718                PackageInfo app = null;
9719                try {
9720                    app = mPackageManager.getPackageInfo(packageName, 0);
9721                } catch (NameNotFoundException nnf) {
9722                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9723                    throw new IllegalArgumentException("Package " + packageName + " not found");
9724                }
9725
9726                if (app.applicationInfo.uid == Binder.getCallingUid()) {
9727                    // So: using the current active transport, and the caller has asked
9728                    // that its own package will be restored.  In this narrow use case
9729                    // we do not require the caller to hold the permission.
9730                    needPermission = false;
9731                }
9732            }
9733        }
9734
9735        if (needPermission) {
9736            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9737                    "beginRestoreSession");
9738        } else {
9739            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
9740        }
9741
9742        synchronized(this) {
9743            if (mActiveRestoreSession != null) {
9744                Slog.d(TAG, "Restore session requested but one already active");
9745                return null;
9746            }
9747            mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
9748            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
9749        }
9750        return mActiveRestoreSession;
9751    }
9752
9753    void clearRestoreSession(ActiveRestoreSession currentSession) {
9754        synchronized(this) {
9755            if (currentSession != mActiveRestoreSession) {
9756                Slog.e(TAG, "ending non-current restore session");
9757            } else {
9758                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
9759                mActiveRestoreSession = null;
9760                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9761            }
9762        }
9763    }
9764
9765    // Note that a currently-active backup agent has notified us that it has
9766    // completed the given outstanding asynchronous backup/restore operation.
9767    public void opComplete(int token, long result) {
9768        if (MORE_DEBUG) {
9769            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
9770        }
9771        Operation op = null;
9772        synchronized (mCurrentOpLock) {
9773            op = mCurrentOperations.get(token);
9774            if (op != null) {
9775                op.state = OP_ACKNOWLEDGED;
9776            }
9777            mCurrentOpLock.notifyAll();
9778        }
9779
9780        // The completion callback, if any, is invoked on the handler
9781        if (op != null && op.callback != null) {
9782            Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
9783            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
9784            mBackupHandler.sendMessage(msg);
9785        }
9786    }
9787
9788    public boolean isAppEligibleForBackup(String packageName) {
9789        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9790                "isAppEligibleForBackup");
9791        try {
9792            PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
9793                    PackageManager.GET_SIGNATURES);
9794            if (!appIsEligibleForBackup(packageInfo.applicationInfo) ||
9795                    appIsStopped(packageInfo.applicationInfo)) {
9796                return false;
9797            }
9798            IBackupTransport transport = getTransport(mCurrentTransport);
9799            if (transport != null) {
9800                try {
9801                    return transport.isAppEligibleForBackup(packageInfo,
9802                        appGetsFullBackup(packageInfo));
9803                } catch (RemoteException e) {
9804                    Slog.e(TAG, "Unable to contact transport");
9805                }
9806            }
9807            // If transport is not present we couldn't tell that the package is not eligible.
9808            return true;
9809        } catch (NameNotFoundException e) {
9810            return false;
9811        }
9812    }
9813
9814    // ----- Restore session -----
9815
9816    class ActiveRestoreSession extends IRestoreSession.Stub {
9817        private static final String TAG = "RestoreSession";
9818
9819        private String mPackageName;
9820        private IBackupTransport mRestoreTransport = null;
9821        RestoreSet[] mRestoreSets = null;
9822        boolean mEnded = false;
9823        boolean mTimedOut = false;
9824
9825        ActiveRestoreSession(String packageName, String transport) {
9826            mPackageName = packageName;
9827            mRestoreTransport = getTransport(transport);
9828        }
9829
9830        public void markTimedOut() {
9831            mTimedOut = true;
9832        }
9833
9834        // --- Binder interface ---
9835        public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
9836            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9837                    "getAvailableRestoreSets");
9838            if (observer == null) {
9839                throw new IllegalArgumentException("Observer must not be null");
9840            }
9841
9842            if (mEnded) {
9843                throw new IllegalStateException("Restore session already ended");
9844            }
9845
9846            if (mTimedOut) {
9847                Slog.i(TAG, "Session already timed out");
9848                return -1;
9849            }
9850
9851            long oldId = Binder.clearCallingIdentity();
9852            try {
9853                if (mRestoreTransport == null) {
9854                    Slog.w(TAG, "Null transport getting restore sets");
9855                    return -1;
9856                }
9857
9858                // We know we're doing legit work now, so halt the timeout
9859                // until we're done.  It gets started again when the result
9860                // comes in.
9861                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9862
9863                // spin off the transport request to our service thread
9864                mWakelock.acquire();
9865                Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
9866                        new RestoreGetSetsParams(mRestoreTransport, this, observer));
9867                mBackupHandler.sendMessage(msg);
9868                return 0;
9869            } catch (Exception e) {
9870                Slog.e(TAG, "Error in getAvailableRestoreSets", e);
9871                return -1;
9872            } finally {
9873                Binder.restoreCallingIdentity(oldId);
9874            }
9875        }
9876
9877        public synchronized int restoreAll(long token, IRestoreObserver observer) {
9878            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9879                    "performRestore");
9880
9881            if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
9882                    + " observer=" + observer);
9883
9884            if (mEnded) {
9885                throw new IllegalStateException("Restore session already ended");
9886            }
9887
9888            if (mTimedOut) {
9889                Slog.i(TAG, "Session already timed out");
9890                return -1;
9891            }
9892
9893            if (mRestoreTransport == null || mRestoreSets == null) {
9894                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9895                return -1;
9896            }
9897
9898            if (mPackageName != null) {
9899                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9900                return -1;
9901            }
9902
9903            String dirName;
9904            try {
9905                dirName = mRestoreTransport.transportDirName();
9906            } catch (RemoteException e) {
9907                // Transport went AWOL; fail.
9908                Slog.e(TAG, "Unable to contact transport for restore");
9909                return -1;
9910            }
9911
9912            synchronized (mQueueLock) {
9913                for (int i = 0; i < mRestoreSets.length; i++) {
9914                    if (token == mRestoreSets[i].token) {
9915                        // Real work, so stop the session timeout until we finalize the restore
9916                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9917
9918                        long oldId = Binder.clearCallingIdentity();
9919                        mWakelock.acquire();
9920                        if (MORE_DEBUG) {
9921                            Slog.d(TAG, "restoreAll() kicking off");
9922                        }
9923                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9924                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
9925                                observer, token);
9926                        mBackupHandler.sendMessage(msg);
9927                        Binder.restoreCallingIdentity(oldId);
9928                        return 0;
9929                    }
9930                }
9931            }
9932
9933            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9934            return -1;
9935        }
9936
9937        // Restores of more than a single package are treated as 'system' restores
9938        public synchronized int restoreSome(long token, IRestoreObserver observer,
9939                String[] packages) {
9940            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9941                    "performRestore");
9942
9943            if (DEBUG) {
9944                StringBuilder b = new StringBuilder(128);
9945                b.append("restoreSome token=");
9946                b.append(Long.toHexString(token));
9947                b.append(" observer=");
9948                b.append(observer.toString());
9949                b.append(" packages=");
9950                if (packages == null) {
9951                    b.append("null");
9952                } else {
9953                    b.append('{');
9954                    boolean first = true;
9955                    for (String s : packages) {
9956                        if (!first) {
9957                            b.append(", ");
9958                        } else first = false;
9959                        b.append(s);
9960                    }
9961                    b.append('}');
9962                }
9963                Slog.d(TAG, b.toString());
9964            }
9965
9966            if (mEnded) {
9967                throw new IllegalStateException("Restore session already ended");
9968            }
9969
9970            if (mTimedOut) {
9971                Slog.i(TAG, "Session already timed out");
9972                return -1;
9973            }
9974
9975            if (mRestoreTransport == null || mRestoreSets == null) {
9976                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9977                return -1;
9978            }
9979
9980            if (mPackageName != null) {
9981                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9982                return -1;
9983            }
9984
9985            String dirName;
9986            try {
9987                dirName = mRestoreTransport.transportDirName();
9988            } catch (RemoteException e) {
9989                // Transport went AWOL; fail.
9990                Slog.e(TAG, "Unable to contact transport for restore");
9991                return -1;
9992            }
9993
9994            synchronized (mQueueLock) {
9995                for (int i = 0; i < mRestoreSets.length; i++) {
9996                    if (token == mRestoreSets[i].token) {
9997                        // Stop the session timeout until we finalize the restore
9998                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9999
10000                        long oldId = Binder.clearCallingIdentity();
10001                        mWakelock.acquire();
10002                        if (MORE_DEBUG) {
10003                            Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
10004                        }
10005                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
10006                        msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
10007                                packages, packages.length > 1);
10008                        mBackupHandler.sendMessage(msg);
10009                        Binder.restoreCallingIdentity(oldId);
10010                        return 0;
10011                    }
10012                }
10013            }
10014
10015            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
10016            return -1;
10017        }
10018
10019        public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
10020            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
10021
10022            if (mEnded) {
10023                throw new IllegalStateException("Restore session already ended");
10024            }
10025
10026            if (mTimedOut) {
10027                Slog.i(TAG, "Session already timed out");
10028                return -1;
10029            }
10030
10031            if (mPackageName != null) {
10032                if (! mPackageName.equals(packageName)) {
10033                    Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
10034                            + " on session for package " + mPackageName);
10035                    return -1;
10036                }
10037            }
10038
10039            PackageInfo app = null;
10040            try {
10041                app = mPackageManager.getPackageInfo(packageName, 0);
10042            } catch (NameNotFoundException nnf) {
10043                Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
10044                return -1;
10045            }
10046
10047            // If the caller is not privileged and is not coming from the target
10048            // app's uid, throw a permission exception back to the caller.
10049            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
10050                    Binder.getCallingPid(), Binder.getCallingUid());
10051            if ((perm == PackageManager.PERMISSION_DENIED) &&
10052                    (app.applicationInfo.uid != Binder.getCallingUid())) {
10053                Slog.w(TAG, "restorePackage: bad packageName=" + packageName
10054                        + " or calling uid=" + Binder.getCallingUid());
10055                throw new SecurityException("No permission to restore other packages");
10056            }
10057
10058            // So far so good; we're allowed to try to restore this package.
10059            long oldId = Binder.clearCallingIdentity();
10060            try {
10061                // Check whether there is data for it in the current dataset, falling back
10062                // to the ancestral dataset if not.
10063                long token = getAvailableRestoreToken(packageName);
10064                if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName
10065                        + " token=" + Long.toHexString(token));
10066
10067                // If we didn't come up with a place to look -- no ancestral dataset and
10068                // the app has never been backed up from this device -- there's nothing
10069                // to do but return failure.
10070                if (token == 0) {
10071                    if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
10072                    return -1;
10073                }
10074
10075                String dirName;
10076                try {
10077                    dirName = mRestoreTransport.transportDirName();
10078                } catch (RemoteException e) {
10079                    // Transport went AWOL; fail.
10080                    Slog.e(TAG, "Unable to contact transport for restore");
10081                    return -1;
10082                }
10083
10084                // Stop the session timeout until we finalize the restore
10085                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
10086
10087                // Ready to go:  enqueue the restore request and claim success
10088                mWakelock.acquire();
10089                if (MORE_DEBUG) {
10090                    Slog.d(TAG, "restorePackage() : " + packageName);
10091                }
10092                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
10093                msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app);
10094                mBackupHandler.sendMessage(msg);
10095            } finally {
10096                Binder.restoreCallingIdentity(oldId);
10097            }
10098            return 0;
10099        }
10100
10101        // Posted to the handler to tear down a restore session in a cleanly synchronized way
10102        class EndRestoreRunnable implements Runnable {
10103            BackupManagerService mBackupManager;
10104            ActiveRestoreSession mSession;
10105
10106            EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
10107                mBackupManager = manager;
10108                mSession = session;
10109            }
10110
10111            public void run() {
10112                // clean up the session's bookkeeping
10113                synchronized (mSession) {
10114                    mSession.mRestoreTransport = null;
10115                    mSession.mEnded = true;
10116                }
10117
10118                // clean up the BackupManagerImpl side of the bookkeeping
10119                // and cancel any pending timeout message
10120                mBackupManager.clearRestoreSession(mSession);
10121            }
10122        }
10123
10124        public synchronized void endRestoreSession() {
10125            if (DEBUG) Slog.d(TAG, "endRestoreSession");
10126
10127            if (mTimedOut) {
10128                Slog.i(TAG, "Session already timed out");
10129                return;
10130            }
10131
10132            if (mEnded) {
10133                throw new IllegalStateException("Restore session already ended");
10134            }
10135
10136            mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
10137        }
10138    }
10139
10140    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
10141        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
10142
10143        long identityToken = Binder.clearCallingIdentity();
10144        try {
10145            if (args != null) {
10146                for (String arg : args) {
10147                    if ("-h".equals(arg)) {
10148                        pw.println("'dumpsys backup' optional arguments:");
10149                        pw.println("  -h       : this help text");
10150                        pw.println("  a[gents] : dump information about defined backup agents");
10151                        return;
10152                    } else if ("agents".startsWith(arg)) {
10153                        dumpAgents(pw);
10154                        return;
10155                    }
10156                }
10157            }
10158            dumpInternal(pw);
10159        } finally {
10160            Binder.restoreCallingIdentity(identityToken);
10161        }
10162    }
10163
10164    private void dumpAgents(PrintWriter pw) {
10165        List<PackageInfo> agentPackages = allAgentPackages();
10166        pw.println("Defined backup agents:");
10167        for (PackageInfo pkg : agentPackages) {
10168            pw.print("  ");
10169            pw.print(pkg.packageName); pw.println(':');
10170            pw.print("      "); pw.println(pkg.applicationInfo.backupAgentName);
10171        }
10172    }
10173
10174    private void dumpInternal(PrintWriter pw) {
10175        synchronized (mQueueLock) {
10176            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
10177                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
10178                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
10179            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
10180            if (mBackupRunning) pw.println("Backup currently running");
10181            pw.println("Last backup pass started: " + mLastBackupPass
10182                    + " (now = " + System.currentTimeMillis() + ')');
10183            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
10184
10185            pw.println("Available transports:");
10186            final String[] transports = listAllTransports();
10187            if (transports != null) {
10188                for (String t : listAllTransports()) {
10189                    pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
10190                    try {
10191                        IBackupTransport transport = getTransport(t);
10192                        File dir = new File(mBaseStateDir, transport.transportDirName());
10193                        pw.println("       destination: " + transport.currentDestinationString());
10194                        pw.println("       intent: " + transport.configurationIntent());
10195                        for (File f : dir.listFiles()) {
10196                            pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
10197                        }
10198                    } catch (Exception e) {
10199                        Slog.e(TAG, "Error in transport", e);
10200                        pw.println("        Error: " + e);
10201                    }
10202                }
10203            }
10204
10205            pw.println("Pending init: " + mPendingInits.size());
10206            for (String s : mPendingInits) {
10207                pw.println("    " + s);
10208            }
10209
10210            if (DEBUG_BACKUP_TRACE) {
10211                synchronized (mBackupTrace) {
10212                    if (!mBackupTrace.isEmpty()) {
10213                        pw.println("Most recent backup trace:");
10214                        for (String s : mBackupTrace) {
10215                            pw.println("   " + s);
10216                        }
10217                    }
10218                }
10219            }
10220
10221            pw.print("Ancestral: "); pw.println(Long.toHexString(mAncestralToken));
10222            pw.print("Current:   "); pw.println(Long.toHexString(mCurrentToken));
10223
10224            int N = mBackupParticipants.size();
10225            pw.println("Participants:");
10226            for (int i=0; i<N; i++) {
10227                int uid = mBackupParticipants.keyAt(i);
10228                pw.print("  uid: ");
10229                pw.println(uid);
10230                HashSet<String> participants = mBackupParticipants.valueAt(i);
10231                for (String app: participants) {
10232                    pw.println("    " + app);
10233                }
10234            }
10235
10236            pw.println("Ancestral packages: "
10237                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
10238            if (mAncestralPackages != null) {
10239                for (String pkg : mAncestralPackages) {
10240                    pw.println("    " + pkg);
10241                }
10242            }
10243
10244            pw.println("Ever backed up: " + mEverStoredApps.size());
10245            for (String pkg : mEverStoredApps) {
10246                pw.println("    " + pkg);
10247            }
10248
10249            pw.println("Pending key/value backup: " + mPendingBackups.size());
10250            for (BackupRequest req : mPendingBackups.values()) {
10251                pw.println("    " + req);
10252            }
10253
10254            pw.println("Full backup queue:" + mFullBackupQueue.size());
10255            for (FullBackupEntry entry : mFullBackupQueue) {
10256                pw.print("    "); pw.print(entry.lastBackup);
10257                pw.print(" : "); pw.println(entry.packageName);
10258            }
10259        }
10260    }
10261
10262    private static void sendBackupOnUpdate(IBackupObserver observer, String packageName,
10263            BackupProgress progress) {
10264        if (observer != null) {
10265            try {
10266                observer.onUpdate(packageName, progress);
10267            } catch (RemoteException e) {
10268                if (DEBUG) {
10269                    Slog.w(TAG, "Backup observer went away: onUpdate");
10270                }
10271            }
10272        }
10273    }
10274
10275    private static void sendBackupOnPackageResult(IBackupObserver observer, String packageName,
10276            int status) {
10277        if (observer != null) {
10278            try {
10279                observer.onResult(packageName, status);
10280            } catch (RemoteException e) {
10281                if (DEBUG) {
10282                    Slog.w(TAG, "Backup observer went away: onResult");
10283                }
10284            }
10285        }
10286    }
10287
10288    private static void sendBackupFinished(IBackupObserver observer, int status) {
10289        if (observer != null) {
10290            try {
10291                observer.backupFinished(status);
10292            } catch (RemoteException e) {
10293                if (DEBUG) {
10294                    Slog.w(TAG, "Backup observer went away: backupFinished");
10295                }
10296            }
10297        }
10298    }
10299}
10300