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