BackupManagerService.java revision 590096a0e372f640fb41d4cb97d8bc68fb7b8ea2
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(transportName);
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    // fire off a backup agent, blocking until it attaches or times out
2320    IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
2321        IBackupAgent agent = null;
2322        synchronized(mAgentConnectLock) {
2323            mConnecting = true;
2324            mConnectedAgent = null;
2325            try {
2326                if (mActivityManager.bindBackupAgent(app, mode)) {
2327                    Slog.d(TAG, "awaiting agent for " + app);
2328
2329                    // success; wait for the agent to arrive
2330                    // only wait 10 seconds for the bind to happen
2331                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
2332                    while (mConnecting && mConnectedAgent == null
2333                            && (System.currentTimeMillis() < timeoutMark)) {
2334                        try {
2335                            mAgentConnectLock.wait(5000);
2336                        } catch (InterruptedException e) {
2337                            // just bail
2338                            Slog.w(TAG, "Interrupted: " + e);
2339                            mActivityManager.clearPendingBackup();
2340                            return null;
2341                        }
2342                    }
2343
2344                    // if we timed out with no connect, abort and move on
2345                    if (mConnecting == true) {
2346                        Slog.w(TAG, "Timeout waiting for agent " + app);
2347                        mActivityManager.clearPendingBackup();
2348                        return null;
2349                    }
2350                    if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
2351                    agent = mConnectedAgent;
2352                }
2353            } catch (RemoteException e) {
2354                // can't happen - ActivityManager is local
2355            }
2356        }
2357        return agent;
2358    }
2359
2360    // clear an application's data, blocking until the operation completes or times out
2361    void clearApplicationDataSynchronous(String packageName) {
2362        // Don't wipe packages marked allowClearUserData=false
2363        try {
2364            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
2365            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
2366                if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
2367                        + packageName);
2368                return;
2369            }
2370        } catch (NameNotFoundException e) {
2371            Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
2372            return;
2373        }
2374
2375        ClearDataObserver observer = new ClearDataObserver();
2376
2377        synchronized(mClearDataLock) {
2378            mClearingData = true;
2379            try {
2380                mActivityManager.clearApplicationUserData(packageName, observer, 0);
2381            } catch (RemoteException e) {
2382                // can't happen because the activity manager is in this process
2383            }
2384
2385            // only wait 10 seconds for the clear data to happen
2386            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
2387            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
2388                try {
2389                    mClearDataLock.wait(5000);
2390                } catch (InterruptedException e) {
2391                    // won't happen, but still.
2392                    mClearingData = false;
2393                }
2394            }
2395        }
2396    }
2397
2398    class ClearDataObserver extends IPackageDataObserver.Stub {
2399        public void onRemoveCompleted(String packageName, boolean succeeded) {
2400            synchronized(mClearDataLock) {
2401                mClearingData = false;
2402                mClearDataLock.notifyAll();
2403            }
2404        }
2405    }
2406
2407    // Get the restore-set token for the best-available restore set for this package:
2408    // the active set if possible, else the ancestral one.  Returns zero if none available.
2409    public long getAvailableRestoreToken(String packageName) {
2410        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2411                "getAvailableRestoreToken");
2412
2413        long token = mAncestralToken;
2414        synchronized (mQueueLock) {
2415            if (mEverStoredApps.contains(packageName)) {
2416                if (MORE_DEBUG) {
2417                    Slog.i(TAG, "App in ever-stored, so using current token");
2418                }
2419                token = mCurrentToken;
2420            }
2421        }
2422        if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
2423        return token;
2424    }
2425
2426    public int requestBackup(String[] packages, IBackupObserver observer) {
2427        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
2428
2429        if (packages == null || packages.length < 1) {
2430            Slog.e(TAG, "No packages named for backup request");
2431            sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
2432            throw new IllegalArgumentException("No packages are provided for backup");
2433        }
2434
2435        IBackupTransport transport = getTransport(mCurrentTransport);
2436        if (transport == null) {
2437            sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
2438            return BackupManager.ERROR_TRANSPORT_ABORTED;
2439        }
2440
2441        ArrayList<String> fullBackupList = new ArrayList<>();
2442        ArrayList<String> kvBackupList = new ArrayList<>();
2443        for (String packageName : packages) {
2444            try {
2445                PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
2446                        PackageManager.GET_SIGNATURES);
2447                if (!appIsEligibleForBackup(packageInfo.applicationInfo)) {
2448                    sendBackupOnPackageResult(observer, packageName,
2449                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
2450                    continue;
2451                }
2452                if (appGetsFullBackup(packageInfo)) {
2453                    fullBackupList.add(packageInfo.packageName);
2454                } else {
2455                    kvBackupList.add(packageInfo.packageName);
2456                }
2457            } catch (NameNotFoundException e) {
2458                sendBackupOnPackageResult(observer, packageName,
2459                        BackupManager.ERROR_PACKAGE_NOT_FOUND);
2460            }
2461        }
2462        EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
2463                fullBackupList.size());
2464        if (MORE_DEBUG) {
2465            Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: " +
2466                fullBackupList.size() + " full backups, " + kvBackupList.size() + " k/v backups");
2467        }
2468
2469        String dirName;
2470        try {
2471            dirName = transport.transportDirName();
2472        } catch (RemoteException e) {
2473            Slog.e(TAG, "Transport became unavailable while attempting backup");
2474            sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
2475            return BackupManager.ERROR_TRANSPORT_ABORTED;
2476        }
2477        Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
2478        msg.obj = new BackupParams(transport, dirName, kvBackupList, fullBackupList, observer,
2479                true);
2480        mBackupHandler.sendMessage(msg);
2481        return BackupManager.SUCCESS;
2482    }
2483
2484    // -----
2485    // Interface and methods used by the asynchronous-with-timeout backup/restore operations
2486
2487    interface BackupRestoreTask {
2488        // Execute one tick of whatever state machine the task implements
2489        void execute();
2490
2491        // An operation that wanted a callback has completed
2492        void operationComplete(long result);
2493
2494        // An operation that wanted a callback has timed out
2495        void handleTimeout();
2496    }
2497
2498    void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) {
2499        if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
2500                + " interval=" + interval);
2501        synchronized (mCurrentOpLock) {
2502            mCurrentOperations.put(token, new Operation(OP_PENDING, callback));
2503
2504            Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback);
2505            mBackupHandler.sendMessageDelayed(msg, interval);
2506        }
2507    }
2508
2509    // synchronous waiter case
2510    boolean waitUntilOperationComplete(int token) {
2511        if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
2512                + Integer.toHexString(token));
2513        int finalState = OP_PENDING;
2514        Operation op = null;
2515        synchronized (mCurrentOpLock) {
2516            while (true) {
2517                op = mCurrentOperations.get(token);
2518                if (op == null) {
2519                    // mysterious disappearance: treat as success with no callback
2520                    break;
2521                } else {
2522                    if (op.state == OP_PENDING) {
2523                        try {
2524                            mCurrentOpLock.wait();
2525                        } catch (InterruptedException e) {}
2526                        // When the wait is notified we loop around and recheck the current state
2527                    } else {
2528                        // No longer pending; we're done
2529                        finalState = op.state;
2530                        break;
2531                    }
2532                }
2533            }
2534        }
2535
2536        mBackupHandler.removeMessages(MSG_TIMEOUT);
2537        if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
2538                + " complete: finalState=" + finalState);
2539        return finalState == OP_ACKNOWLEDGED;
2540    }
2541
2542    void handleTimeout(int token, Object obj) {
2543        // Notify any synchronous waiters
2544        Operation op = null;
2545        synchronized (mCurrentOpLock) {
2546            op = mCurrentOperations.get(token);
2547            if (MORE_DEBUG) {
2548                if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token)
2549                        + " but no op found");
2550            }
2551            int state = (op != null) ? op.state : OP_TIMEOUT;
2552            if (state == OP_PENDING) {
2553                if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token));
2554                op.state = OP_TIMEOUT;
2555                mCurrentOperations.put(token, op);
2556            }
2557            mCurrentOpLock.notifyAll();
2558        }
2559
2560        // If there's a TimeoutHandler for this event, call it
2561        if (op != null && op.callback != null) {
2562            op.callback.handleTimeout();
2563        }
2564    }
2565
2566    // ----- Back up a set of applications via a worker thread -----
2567
2568    enum BackupState {
2569        INITIAL,
2570        RUNNING_QUEUE,
2571        FINAL
2572    }
2573
2574    class PerformBackupTask implements BackupRestoreTask {
2575        private static final String TAG = "PerformBackupTask";
2576
2577        IBackupTransport mTransport;
2578        ArrayList<BackupRequest> mQueue;
2579        ArrayList<BackupRequest> mOriginalQueue;
2580        File mStateDir;
2581        File mJournal;
2582        BackupState mCurrentState;
2583        ArrayList<String> mPendingFullBackups;
2584        IBackupObserver mObserver;
2585
2586        // carried information about the current in-flight operation
2587        IBackupAgent mAgentBinder;
2588        PackageInfo mCurrentPackage;
2589        File mSavedStateName;
2590        File mBackupDataName;
2591        File mNewStateName;
2592        ParcelFileDescriptor mSavedState;
2593        ParcelFileDescriptor mBackupData;
2594        ParcelFileDescriptor mNewState;
2595        int mStatus;
2596        boolean mFinished;
2597        boolean mUserInitiated;
2598
2599        public PerformBackupTask(IBackupTransport transport, String dirName,
2600                ArrayList<BackupRequest> queue, File journal, IBackupObserver observer,
2601                ArrayList<String> pendingFullBackups, boolean userInitiated) {
2602            mTransport = transport;
2603            mOriginalQueue = queue;
2604            mJournal = journal;
2605            mObserver = observer;
2606            mPendingFullBackups = pendingFullBackups;
2607            mUserInitiated = userInitiated;
2608
2609            mStateDir = new File(mBaseStateDir, dirName);
2610
2611            mCurrentState = BackupState.INITIAL;
2612            mFinished = false;
2613
2614            addBackupTrace("STATE => INITIAL");
2615        }
2616
2617        // Main entry point: perform one chunk of work, updating the state as appropriate
2618        // and reposting the next chunk to the primary backup handler thread.
2619        @Override
2620        public void execute() {
2621            switch (mCurrentState) {
2622                case INITIAL:
2623                    beginBackup();
2624                    break;
2625
2626                case RUNNING_QUEUE:
2627                    invokeNextAgent();
2628                    break;
2629
2630                case FINAL:
2631                    if (!mFinished) finalizeBackup();
2632                    else {
2633                        Slog.e(TAG, "Duplicate finish");
2634                    }
2635                    mFinished = true;
2636                    break;
2637            }
2638        }
2639
2640        // We're starting a backup pass.  Initialize the transport and send
2641        // the PM metadata blob if we haven't already.
2642        void beginBackup() {
2643            if (DEBUG_BACKUP_TRACE) {
2644                clearBackupTrace();
2645                StringBuilder b = new StringBuilder(256);
2646                b.append("beginBackup: [");
2647                for (BackupRequest req : mOriginalQueue) {
2648                    b.append(' ');
2649                    b.append(req.packageName);
2650                }
2651                b.append(" ]");
2652                addBackupTrace(b.toString());
2653            }
2654
2655            mAgentBinder = null;
2656            mStatus = BackupTransport.TRANSPORT_OK;
2657
2658            // Sanity check: if the queue is empty we have no work to do.
2659            if (mOriginalQueue.isEmpty() && mPendingFullBackups.isEmpty()) {
2660                Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
2661                addBackupTrace("queue empty at begin");
2662                sendBackupFinished(mObserver, BackupManager.SUCCESS);
2663                executeNextState(BackupState.FINAL);
2664                return;
2665            }
2666
2667            // We need to retain the original queue contents in case of transport
2668            // failure, but we want a working copy that we can manipulate along
2669            // the way.
2670            mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
2671
2672            // The app metadata pseudopackage might also be represented in the
2673            // backup queue if apps have been added/removed since the last time
2674            // we performed a backup.  Drop it from the working queue now that
2675            // we're committed to evaluating it for backup regardless.
2676            for (int i = 0; i < mQueue.size(); i++) {
2677                if (PACKAGE_MANAGER_SENTINEL.equals(mQueue.get(i).packageName)) {
2678                    if (MORE_DEBUG) {
2679                        Slog.i(TAG, "Metadata in queue; eliding");
2680                    }
2681                    mQueue.remove(i);
2682                    break;
2683                }
2684            }
2685
2686            if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
2687
2688            File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2689            try {
2690                final String transportName = mTransport.transportDirName();
2691                EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
2692
2693                // If we haven't stored package manager metadata yet, we must init the transport.
2694                if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
2695                    Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
2696                    addBackupTrace("initializing transport " + transportName);
2697                    resetBackupState(mStateDir);  // Just to make sure.
2698                    mStatus = mTransport.initializeDevice();
2699
2700                    addBackupTrace("transport.initializeDevice() == " + mStatus);
2701                    if (mStatus == BackupTransport.TRANSPORT_OK) {
2702                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
2703                    } else {
2704                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
2705                        Slog.e(TAG, "Transport error in initializeDevice()");
2706                    }
2707                }
2708
2709                // The package manager doesn't have a proper <application> etc, but since
2710                // it's running here in the system process we can just set up its agent
2711                // directly and use a synthetic BackupRequest.  We always run this pass
2712                // because it's cheap and this way we guarantee that we don't get out of
2713                // step even if we're selecting among various transports at run time.
2714                if (mStatus == BackupTransport.TRANSPORT_OK) {
2715                    PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
2716                            mPackageManager);
2717                    mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
2718                            IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
2719                    addBackupTrace("PMBA invoke: " + mStatus);
2720
2721                    // Because the PMBA is a local instance, it has already executed its
2722                    // backup callback and returned.  Blow away the lingering (spurious)
2723                    // pending timeout message for it.
2724                    mBackupHandler.removeMessages(MSG_TIMEOUT);
2725                }
2726
2727                if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
2728                    // The backend reports that our dataset has been wiped.  Note this in
2729                    // the event log; the no-success code below will reset the backup
2730                    // state as well.
2731                    EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
2732                }
2733            } catch (Exception e) {
2734                Slog.e(TAG, "Error in backup thread", e);
2735                addBackupTrace("Exception in backup thread: " + e);
2736                mStatus = BackupTransport.TRANSPORT_ERROR;
2737            } finally {
2738                // If we've succeeded so far, invokeAgentForBackup() will have run the PM
2739                // metadata and its completion/timeout callback will continue the state
2740                // machine chain.  If it failed that won't happen; we handle that now.
2741                addBackupTrace("exiting prelim: " + mStatus);
2742                if (mStatus != BackupTransport.TRANSPORT_OK) {
2743                    // if things went wrong at this point, we need to
2744                    // restage everything and try again later.
2745                    resetBackupState(mStateDir);  // Just to make sure.
2746                    // In case of any other error, it's backup transport error.
2747                    sendBackupFinished(mObserver, BackupManager.ERROR_TRANSPORT_ABORTED);
2748                    executeNextState(BackupState.FINAL);
2749                }
2750            }
2751        }
2752
2753        // Transport has been initialized and the PM metadata submitted successfully
2754        // if that was warranted.  Now we process the single next thing in the queue.
2755        void invokeNextAgent() {
2756            mStatus = BackupTransport.TRANSPORT_OK;
2757            addBackupTrace("invoke q=" + mQueue.size());
2758
2759            // Sanity check that we have work to do.  If not, skip to the end where
2760            // we reestablish the wakelock invariants etc.
2761            if (mQueue.isEmpty()) {
2762                if (MORE_DEBUG) Slog.i(TAG, "queue now empty");
2763                executeNextState(BackupState.FINAL);
2764                return;
2765            }
2766
2767            // pop the entry we're going to process on this step
2768            BackupRequest request = mQueue.get(0);
2769            mQueue.remove(0);
2770
2771            Slog.d(TAG, "starting key/value backup of " + request);
2772            addBackupTrace("launch agent for " + request.packageName);
2773
2774            // Verify that the requested app exists; it might be something that
2775            // requested a backup but was then uninstalled.  The request was
2776            // journalled and rather than tamper with the journal it's safer
2777            // to sanity-check here.  This also gives us the classname of the
2778            // package's backup agent.
2779            try {
2780                mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
2781                        PackageManager.GET_SIGNATURES);
2782                if (!appIsEligibleForBackup(mCurrentPackage.applicationInfo)) {
2783                    // The manifest has changed but we had a stale backup request pending.
2784                    // This won't happen again because the app won't be requesting further
2785                    // backups.
2786                    Slog.i(TAG, "Package " + request.packageName
2787                            + " no longer supports backup; skipping");
2788                    addBackupTrace("skipping - not eligible, completion is noop");
2789                    // Shouldn't happen in case of requested backup, as pre-check was done in
2790                    // #requestBackup(), except to app update done concurrently
2791                    sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2792                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
2793                    executeNextState(BackupState.RUNNING_QUEUE);
2794                    return;
2795                }
2796
2797                if (appGetsFullBackup(mCurrentPackage)) {
2798                    // It's possible that this app *formerly* was enqueued for key/value backup,
2799                    // but has since been updated and now only supports the full-data path.
2800                    // Don't proceed with a key/value backup for it in this case.
2801                    Slog.i(TAG, "Package " + request.packageName
2802                            + " requests full-data rather than key/value; skipping");
2803                    addBackupTrace("skipping - fullBackupOnly, completion is noop");
2804                    // Shouldn't happen in case of requested backup, as pre-check was done in
2805                    // #requestBackup()
2806                    sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2807                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
2808                    executeNextState(BackupState.RUNNING_QUEUE);
2809                    return;
2810                }
2811
2812                if (appIsStopped(mCurrentPackage.applicationInfo)) {
2813                    // The app has been force-stopped or cleared or just installed,
2814                    // and not yet launched out of that state, so just as it won't
2815                    // receive broadcasts, we won't run it for backup.
2816                    addBackupTrace("skipping - stopped");
2817                    sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2818                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
2819                    executeNextState(BackupState.RUNNING_QUEUE);
2820                    return;
2821                }
2822
2823                IBackupAgent agent = null;
2824                try {
2825                    mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
2826                    agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
2827                            IApplicationThread.BACKUP_MODE_INCREMENTAL);
2828                    addBackupTrace("agent bound; a? = " + (agent != null));
2829                    if (agent != null) {
2830                        mAgentBinder = agent;
2831                        mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
2832                        // at this point we'll either get a completion callback from the
2833                        // agent, or a timeout message on the main handler.  either way, we're
2834                        // done here as long as we're successful so far.
2835                    } else {
2836                        // Timeout waiting for the agent
2837                        mStatus = BackupTransport.AGENT_ERROR;
2838                    }
2839                } catch (SecurityException ex) {
2840                    // Try for the next one.
2841                    Slog.d(TAG, "error in bind/backup", ex);
2842                    mStatus = BackupTransport.AGENT_ERROR;
2843                            addBackupTrace("agent SE");
2844                }
2845            } catch (NameNotFoundException e) {
2846                Slog.d(TAG, "Package does not exist; skipping");
2847                addBackupTrace("no such package");
2848                mStatus = BackupTransport.AGENT_UNKNOWN;
2849            } finally {
2850                mWakelock.setWorkSource(null);
2851
2852                // If there was an agent error, no timeout/completion handling will occur.
2853                // That means we need to direct to the next state ourselves.
2854                if (mStatus != BackupTransport.TRANSPORT_OK) {
2855                    BackupState nextState = BackupState.RUNNING_QUEUE;
2856                    mAgentBinder = null;
2857
2858                    // An agent-level failure means we reenqueue this one agent for
2859                    // a later retry, but otherwise proceed normally.
2860                    if (mStatus == BackupTransport.AGENT_ERROR) {
2861                        if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
2862                                + " - restaging");
2863                        dataChangedImpl(request.packageName);
2864                        mStatus = BackupTransport.TRANSPORT_OK;
2865                        if (mQueue.isEmpty()) nextState = BackupState.FINAL;
2866                        sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2867                                BackupManager.ERROR_AGENT_FAILURE);
2868                    } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
2869                        // Failed lookup of the app, so we couldn't bring up an agent, but
2870                        // we're otherwise fine.  Just drop it and go on to the next as usual.
2871                        mStatus = BackupTransport.TRANSPORT_OK;
2872                        sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
2873                                BackupManager.ERROR_PACKAGE_NOT_FOUND);
2874                    } else {
2875                        // Transport-level failure means we reenqueue everything
2876                        revertAndEndBackup();
2877                        nextState = BackupState.FINAL;
2878                    }
2879
2880                    executeNextState(nextState);
2881                } else {
2882                    // success case
2883                    addBackupTrace("expecting completion/timeout callback");
2884                }
2885            }
2886        }
2887
2888        void finalizeBackup() {
2889            addBackupTrace("finishing");
2890
2891            // Either backup was successful, in which case we of course do not need
2892            // this pass's journal any more; or it failed, in which case we just
2893            // re-enqueued all of these packages in the current active journal.
2894            // Either way, we no longer need this pass's journal.
2895            if (mJournal != null && !mJournal.delete()) {
2896                Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
2897            }
2898
2899            // If everything actually went through and this is the first time we've
2900            // done a backup, we can now record what the current backup dataset token
2901            // is.
2902            if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) {
2903                addBackupTrace("success; recording token");
2904                try {
2905                    mCurrentToken = mTransport.getCurrentRestoreSet();
2906                    writeRestoreTokens();
2907                } catch (RemoteException e) {
2908                    // nothing for it at this point, unfortunately, but this will be
2909                    // recorded the next time we fully succeed.
2910                    addBackupTrace("transport threw returning token");
2911                }
2912            }
2913
2914            // Set up the next backup pass - at this point we can set mBackupRunning
2915            // to false to allow another pass to fire, because we're done with the
2916            // state machine sequence and the wakelock is refcounted.
2917            synchronized (mQueueLock) {
2918                mBackupRunning = false;
2919                if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
2920                    // Make sure we back up everything and perform the one-time init
2921                    if (MORE_DEBUG) Slog.d(TAG, "Server requires init; rerunning");
2922                    addBackupTrace("init required; rerunning");
2923                    try {
2924                        mPendingInits.add(mTransport.transportDirName());
2925                    } catch (Exception e) {
2926                        Slog.w(TAG, "Failed to query transport name heading for init", e);
2927                        // swallow it and proceed; we don't rely on this
2928                    }
2929                    clearMetadata();
2930                    backupNow();
2931                }
2932            }
2933
2934            clearBackupTrace();
2935
2936            if (mStatus == BackupTransport.TRANSPORT_OK &&
2937                    mPendingFullBackups != null && !mPendingFullBackups.isEmpty()) {
2938                Slog.d(TAG, "Starting full backups for: " + mPendingFullBackups);
2939                CountDownLatch latch = new CountDownLatch(1);
2940                String[] fullBackups =
2941                        mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
2942                PerformFullTransportBackupTask task =
2943                        new PerformFullTransportBackupTask(/*fullBackupRestoreObserver*/ null,
2944                                fullBackups, /*updateSchedule*/ false, /*runningJob*/ null, latch,
2945                                mObserver, mUserInitiated);
2946                // Acquiring wakelock for PerformFullTransportBackupTask before its start.
2947                mWakelock.acquire();
2948                (new Thread(task, "full-transport-requested")).start();
2949            } else {
2950                switch (mStatus) {
2951                    case BackupTransport.TRANSPORT_OK:
2952                        sendBackupFinished(mObserver, BackupManager.SUCCESS);
2953                        break;
2954                    case BackupTransport.TRANSPORT_NOT_INITIALIZED:
2955                        sendBackupFinished(mObserver, BackupManager.ERROR_TRANSPORT_ABORTED);
2956                        break;
2957                    case BackupTransport.TRANSPORT_ERROR:
2958                    default:
2959                        sendBackupFinished(mObserver, BackupManager.ERROR_TRANSPORT_ABORTED);
2960                        break;
2961                }
2962            }
2963            Slog.i(BackupManagerService.TAG, "K/V backup pass finished.");
2964            // Only once we're entirely finished do we release the wakelock for k/v backup.
2965            mWakelock.release();
2966        }
2967
2968        // Remove the PM metadata state. This will generate an init on the next pass.
2969        void clearMetadata() {
2970            final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2971            if (pmState.exists()) pmState.delete();
2972        }
2973
2974        // Invoke an agent's doBackup() and start a timeout message spinning on the main
2975        // handler in case it doesn't get back to us.
2976        int invokeAgentForBackup(String packageName, IBackupAgent agent,
2977                IBackupTransport transport) {
2978            if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName);
2979            addBackupTrace("invoking " + packageName);
2980
2981            mSavedStateName = new File(mStateDir, packageName);
2982            mBackupDataName = new File(mDataDir, packageName + ".data");
2983            mNewStateName = new File(mStateDir, packageName + ".new");
2984            if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName);
2985
2986            mSavedState = null;
2987            mBackupData = null;
2988            mNewState = null;
2989
2990            final int token = generateToken();
2991            try {
2992                // Look up the package info & signatures.  This is first so that if it
2993                // throws an exception, there's no file setup yet that would need to
2994                // be unraveled.
2995                if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
2996                    // The metadata 'package' is synthetic; construct one and make
2997                    // sure our global state is pointed at it
2998                    mCurrentPackage = new PackageInfo();
2999                    mCurrentPackage.packageName = packageName;
3000                }
3001
3002                // In a full backup, we pass a null ParcelFileDescriptor as
3003                // the saved-state "file". This is by definition an incremental,
3004                // so we build a saved state file to pass.
3005                mSavedState = ParcelFileDescriptor.open(mSavedStateName,
3006                        ParcelFileDescriptor.MODE_READ_ONLY |
3007                        ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
3008
3009                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
3010                        ParcelFileDescriptor.MODE_READ_WRITE |
3011                        ParcelFileDescriptor.MODE_CREATE |
3012                        ParcelFileDescriptor.MODE_TRUNCATE);
3013
3014                if (!SELinux.restorecon(mBackupDataName)) {
3015                    Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
3016                }
3017
3018                mNewState = ParcelFileDescriptor.open(mNewStateName,
3019                        ParcelFileDescriptor.MODE_READ_WRITE |
3020                        ParcelFileDescriptor.MODE_CREATE |
3021                        ParcelFileDescriptor.MODE_TRUNCATE);
3022
3023                // Initiate the target's backup pass
3024                addBackupTrace("setting timeout");
3025                prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
3026                addBackupTrace("calling agent doBackup()");
3027                agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder);
3028            } catch (Exception e) {
3029                Slog.e(TAG, "Error invoking for backup on " + packageName);
3030                addBackupTrace("exception: " + e);
3031                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
3032                        e.toString());
3033                agentErrorCleanup();
3034                return BackupTransport.AGENT_ERROR;
3035            }
3036
3037            // At this point the agent is off and running.  The next thing to happen will
3038            // either be a callback from the agent, at which point we'll process its data
3039            // for transport, or a timeout.  Either way the next phase will happen in
3040            // response to the TimeoutHandler interface callbacks.
3041            addBackupTrace("invoke success");
3042            return BackupTransport.TRANSPORT_OK;
3043        }
3044
3045        public void failAgent(IBackupAgent agent, String message) {
3046            try {
3047                agent.fail(message);
3048            } catch (Exception e) {
3049                Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName);
3050            }
3051        }
3052
3053        // SHA-1 a byte array and return the result in hex
3054        private String SHA1Checksum(byte[] input) {
3055            final byte[] checksum;
3056            try {
3057                MessageDigest md = MessageDigest.getInstance("SHA-1");
3058                checksum = md.digest(input);
3059            } catch (NoSuchAlgorithmException e) {
3060                Slog.e(TAG, "Unable to use SHA-1!");
3061                return "00";
3062            }
3063
3064            StringBuffer sb = new StringBuffer(checksum.length * 2);
3065            for (int i = 0; i < checksum.length; i++) {
3066                sb.append(Integer.toHexString(checksum[i]));
3067            }
3068            return sb.toString();
3069        }
3070
3071        private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
3072                throws IOException {
3073            // TODO: http://b/22388012
3074            byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
3075                    UserHandle.USER_SYSTEM);
3076            // has the widget state changed since last time?
3077            final File widgetFile = new File(mStateDir, pkgName + "_widget");
3078            final boolean priorStateExists = widgetFile.exists();
3079
3080            if (MORE_DEBUG) {
3081                if (priorStateExists || widgetState != null) {
3082                    Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
3083                            + " prior=" + priorStateExists);
3084                }
3085            }
3086
3087            if (!priorStateExists && widgetState == null) {
3088                // no prior state, no new state => nothing to do
3089                return;
3090            }
3091
3092            // if the new state is not null, we might need to compare checksums to
3093            // determine whether to update the widget blob in the archive.  If the
3094            // widget state *is* null, we know a priori at this point that we simply
3095            // need to commit a deletion for it.
3096            String newChecksum = null;
3097            if (widgetState != null) {
3098                newChecksum = SHA1Checksum(widgetState);
3099                if (priorStateExists) {
3100                    final String priorChecksum;
3101                    try (
3102                        FileInputStream fin = new FileInputStream(widgetFile);
3103                        DataInputStream in = new DataInputStream(fin)
3104                    ) {
3105                        priorChecksum = in.readUTF();
3106                    }
3107                    if (Objects.equals(newChecksum, priorChecksum)) {
3108                        // Same checksum => no state change => don't rewrite the widget data
3109                        return;
3110                    }
3111                }
3112            } // else widget state *became* empty, so we need to commit a deletion
3113
3114            BackupDataOutput out = new BackupDataOutput(fd);
3115            if (widgetState != null) {
3116                try (
3117                    FileOutputStream fout = new FileOutputStream(widgetFile);
3118                    DataOutputStream stateOut = new DataOutputStream(fout)
3119                ) {
3120                    stateOut.writeUTF(newChecksum);
3121                }
3122
3123                out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
3124                out.writeEntityData(widgetState, widgetState.length);
3125            } else {
3126                // Widget state for this app has been removed; commit a deletion
3127                out.writeEntityHeader(KEY_WIDGET_STATE, -1);
3128                widgetFile.delete();
3129            }
3130        }
3131
3132        @Override
3133        public void operationComplete(long unusedResult) {
3134            // The agent reported back to us!
3135
3136            if (mBackupData == null) {
3137                // This callback was racing with our timeout, so we've cleaned up the
3138                // agent state already and are on to the next thing.  We have nothing
3139                // further to do here: agent state having been cleared means that we've
3140                // initiated the appropriate next operation.
3141                final String pkg = (mCurrentPackage != null)
3142                        ? mCurrentPackage.packageName : "[none]";
3143                if (MORE_DEBUG) {
3144                    Slog.i(TAG, "Callback after agent teardown: " + pkg);
3145                }
3146                addBackupTrace("late opComplete; curPkg = " + pkg);
3147                return;
3148            }
3149
3150            final String pkgName = mCurrentPackage.packageName;
3151            final long filepos = mBackupDataName.length();
3152            FileDescriptor fd = mBackupData.getFileDescriptor();
3153            try {
3154                // If it's a 3rd party app, see whether they wrote any protected keys
3155                // and complain mightily if they are attempting shenanigans.
3156                if (mCurrentPackage.applicationInfo != null &&
3157                        (mCurrentPackage.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
3158                    ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
3159                            ParcelFileDescriptor.MODE_READ_ONLY);
3160                    BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
3161                    try {
3162                        while (in.readNextHeader()) {
3163                            final String key = in.getKey();
3164                            if (key != null && key.charAt(0) >= 0xff00) {
3165                                // Not okay: crash them and bail.
3166                                failAgent(mAgentBinder, "Illegal backup key: " + key);
3167                                addBackupTrace("illegal key " + key + " from " + pkgName);
3168                                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
3169                                        "bad key");
3170                                mBackupHandler.removeMessages(MSG_TIMEOUT);
3171                                sendBackupOnPackageResult(mObserver, pkgName,
3172                                        BackupManager.ERROR_AGENT_FAILURE);
3173                                agentErrorCleanup();
3174                                // agentErrorCleanup() implicitly executes next state properly
3175                                return;
3176                            }
3177                            in.skipEntityData();
3178                        }
3179                    } finally {
3180                        if (readFd != null) {
3181                            readFd.close();
3182                        }
3183                    }
3184                }
3185
3186                // Piggyback the widget state payload, if any
3187                writeWidgetPayloadIfAppropriate(fd, pkgName);
3188            } catch (IOException e) {
3189                // Hard disk error; recovery/failure policy TBD.  For now roll back,
3190                // but we may want to consider this a transport-level failure (i.e.
3191                // we're in such a bad state that we can't contemplate doing backup
3192                // operations any more during this pass).
3193                Slog.w(TAG, "Unable to save widget state for " + pkgName);
3194                try {
3195                    Os.ftruncate(fd, filepos);
3196                } catch (ErrnoException ee) {
3197                    Slog.w(TAG, "Unable to roll back!");
3198                }
3199            }
3200
3201            // Spin the data off to the transport and proceed with the next stage.
3202            if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
3203                    + pkgName);
3204            mBackupHandler.removeMessages(MSG_TIMEOUT);
3205            clearAgentState();
3206            addBackupTrace("operation complete");
3207
3208            ParcelFileDescriptor backupData = null;
3209            mStatus = BackupTransport.TRANSPORT_OK;
3210            long size = 0;
3211            try {
3212                size = mBackupDataName.length();
3213                if (size > 0) {
3214                    if (mStatus == BackupTransport.TRANSPORT_OK) {
3215                        backupData = ParcelFileDescriptor.open(mBackupDataName,
3216                                ParcelFileDescriptor.MODE_READ_ONLY);
3217                        addBackupTrace("sending data to transport");
3218                        int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
3219                        mStatus = mTransport.performBackup(mCurrentPackage, backupData, flags);
3220                    }
3221
3222                    // TODO - We call finishBackup() for each application backed up, because
3223                    // we need to know now whether it succeeded or failed.  Instead, we should
3224                    // hold off on finishBackup() until the end, which implies holding off on
3225                    // renaming *all* the output state files (see below) until that happens.
3226
3227                    addBackupTrace("data delivered: " + mStatus);
3228                    if (mStatus == BackupTransport.TRANSPORT_OK) {
3229                        addBackupTrace("finishing op on transport");
3230                        mStatus = mTransport.finishBackup();
3231                        addBackupTrace("finished: " + mStatus);
3232                    } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3233                        addBackupTrace("transport rejected package");
3234                    }
3235                } else {
3236                    if (MORE_DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
3237                    addBackupTrace("no data to send");
3238                }
3239
3240                if (mStatus == BackupTransport.TRANSPORT_OK) {
3241                    // After successful transport, delete the now-stale data
3242                    // and juggle the files so that next time we supply the agent
3243                    // with the new state file it just created.
3244                    mBackupDataName.delete();
3245                    mNewStateName.renameTo(mSavedStateName);
3246                    sendBackupOnPackageResult(mObserver, pkgName, BackupManager.SUCCESS);
3247                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
3248                    logBackupComplete(pkgName);
3249                } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3250                    // The transport has rejected backup of this specific package.  Roll it
3251                    // back but proceed with running the rest of the queue.
3252                    mBackupDataName.delete();
3253                    mNewStateName.delete();
3254                    sendBackupOnPackageResult(mObserver, pkgName,
3255                            BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
3256                    EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
3257                } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
3258                    sendBackupOnPackageResult(mObserver, pkgName,
3259                            BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
3260                    EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName);
3261                } else {
3262                    // Actual transport-level failure to communicate the data to the backend
3263                    sendBackupOnPackageResult(mObserver, pkgName,
3264                            BackupManager.ERROR_TRANSPORT_ABORTED);
3265                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
3266                }
3267            } catch (Exception e) {
3268                sendBackupOnPackageResult(mObserver, pkgName,
3269                        BackupManager.ERROR_TRANSPORT_ABORTED);
3270                Slog.e(TAG, "Transport error backing up " + pkgName, e);
3271                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
3272                mStatus = BackupTransport.TRANSPORT_ERROR;
3273            } finally {
3274                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
3275            }
3276
3277            final BackupState nextState;
3278            if (mStatus == BackupTransport.TRANSPORT_OK
3279                    || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3280                // Success or single-package rejection.  Proceed with the next app if any,
3281                // otherwise we're done.
3282                nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
3283            } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
3284                if (MORE_DEBUG) {
3285                    Slog.d(TAG, "Package " + mCurrentPackage.packageName +
3286                            " hit quota limit on k/v backup");
3287                }
3288                if (mAgentBinder != null) {
3289                    try {
3290                        long quota = mTransport.getBackupQuota(mCurrentPackage.packageName, false);
3291                        mAgentBinder.doQuotaExceeded(size, quota);
3292                    } catch (RemoteException e) {
3293                        Slog.e(TAG, "Unable to contact backup agent for quota exceeded");
3294                    }
3295                }
3296                nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
3297            } else {
3298                // Any other error here indicates a transport-level failure.  That means
3299                // we need to halt everything and reschedule everything for next time.
3300                revertAndEndBackup();
3301                nextState = BackupState.FINAL;
3302            }
3303
3304            executeNextState(nextState);
3305        }
3306
3307        @Override
3308        public void handleTimeout() {
3309            // Whoops, the current agent timed out running doBackup().  Tidy up and restage
3310            // it for the next time we run a backup pass.
3311            // !!! TODO: keep track of failure counts per agent, and blacklist those which
3312            // fail repeatedly (i.e. have proved themselves to be buggy).
3313            Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
3314            EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
3315                    "timeout");
3316            addBackupTrace("timeout of " + mCurrentPackage.packageName);
3317            agentErrorCleanup();
3318            dataChangedImpl(mCurrentPackage.packageName);
3319        }
3320
3321        void revertAndEndBackup() {
3322            if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
3323            addBackupTrace("transport error; reverting");
3324
3325            // We want to reset the backup schedule based on whatever the transport suggests
3326            // by way of retry/backoff time.
3327            long delay;
3328            try {
3329                delay = mTransport.requestBackupTime();
3330            } catch (Exception e) {
3331                Slog.w(TAG, "Unable to contact transport for recommended backoff");
3332                delay = 0;  // use the scheduler's default
3333            }
3334            KeyValueBackupJob.schedule(mContext, delay);
3335
3336            for (BackupRequest request : mOriginalQueue) {
3337                dataChangedImpl(request.packageName);
3338            }
3339
3340        }
3341
3342        void agentErrorCleanup() {
3343            mBackupDataName.delete();
3344            mNewStateName.delete();
3345            clearAgentState();
3346
3347            executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
3348        }
3349
3350        // Cleanup common to both success and failure cases
3351        void clearAgentState() {
3352            try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {}
3353            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
3354            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
3355            synchronized (mCurrentOpLock) {
3356                // Current-operation callback handling requires the validity of these various
3357                // bits of internal state as an invariant of the operation still being live.
3358                // This means we make sure to clear all of the state in unison inside the lock.
3359                mCurrentOperations.clear();
3360                mSavedState = mBackupData = mNewState = null;
3361            }
3362
3363            // If this was a pseudopackage there's no associated Activity Manager state
3364            if (mCurrentPackage.applicationInfo != null) {
3365                addBackupTrace("unbinding " + mCurrentPackage.packageName);
3366                try {  // unbind even on timeout, just in case
3367                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
3368                } catch (RemoteException e) { /* can't happen; activity manager is local */ }
3369            }
3370        }
3371
3372        void executeNextState(BackupState nextState) {
3373            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
3374                    + this + " nextState=" + nextState);
3375            addBackupTrace("executeNextState => " + nextState);
3376            mCurrentState = nextState;
3377            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
3378            mBackupHandler.sendMessage(msg);
3379        }
3380    }
3381
3382
3383    // ----- Full backup/restore to a file/socket -----
3384
3385    class FullBackupObbConnection implements ServiceConnection {
3386        volatile IObbBackupService mService;
3387
3388        FullBackupObbConnection() {
3389            mService = null;
3390        }
3391
3392        public void establish() {
3393            if (MORE_DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this);
3394            Intent obbIntent = new Intent().setComponent(new ComponentName(
3395                    "com.android.sharedstoragebackup",
3396                    "com.android.sharedstoragebackup.ObbBackupService"));
3397            BackupManagerService.this.mContext.bindService(
3398                    obbIntent, this, Context.BIND_AUTO_CREATE);
3399        }
3400
3401        public void tearDown() {
3402            BackupManagerService.this.mContext.unbindService(this);
3403        }
3404
3405        public boolean backupObbs(PackageInfo pkg, OutputStream out) {
3406            boolean success = false;
3407            waitForConnection();
3408
3409            ParcelFileDescriptor[] pipes = null;
3410            try {
3411                pipes = ParcelFileDescriptor.createPipe();
3412                int token = generateToken();
3413                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
3414                mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
3415                routeSocketDataToOutput(pipes[0], out);
3416                success = waitUntilOperationComplete(token);
3417            } catch (Exception e) {
3418                Slog.w(TAG, "Unable to back up OBBs for " + pkg, e);
3419            } finally {
3420                try {
3421                    out.flush();
3422                    if (pipes != null) {
3423                        if (pipes[0] != null) pipes[0].close();
3424                        if (pipes[1] != null) pipes[1].close();
3425                    }
3426                } catch (IOException e) {
3427                    Slog.w(TAG, "I/O error closing down OBB backup", e);
3428                }
3429            }
3430            return success;
3431        }
3432
3433        public void restoreObbFile(String pkgName, ParcelFileDescriptor data,
3434                long fileSize, int type, String path, long mode, long mtime,
3435                int token, IBackupManager callbackBinder) {
3436            waitForConnection();
3437
3438            try {
3439                mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime,
3440                        token, callbackBinder);
3441            } catch (Exception e) {
3442                Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e);
3443            }
3444        }
3445
3446        private void waitForConnection() {
3447            synchronized (this) {
3448                while (mService == null) {
3449                    if (MORE_DEBUG) Slog.i(TAG, "...waiting for OBB service binding...");
3450                    try {
3451                        this.wait();
3452                    } catch (InterruptedException e) { /* never interrupted */ }
3453                }
3454                if (MORE_DEBUG) Slog.i(TAG, "Connected to OBB service; continuing");
3455            }
3456        }
3457
3458        @Override
3459        public void onServiceConnected(ComponentName name, IBinder service) {
3460            synchronized (this) {
3461                mService = IObbBackupService.Stub.asInterface(service);
3462                if (MORE_DEBUG) Slog.i(TAG, "OBB service connection " + mService
3463                        + " connected on " + this);
3464                this.notifyAll();
3465            }
3466        }
3467
3468        @Override
3469        public void onServiceDisconnected(ComponentName name) {
3470            synchronized (this) {
3471                mService = null;
3472                if (MORE_DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this);
3473                this.notifyAll();
3474            }
3475        }
3476
3477    }
3478
3479    private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
3480            throws IOException {
3481        FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
3482        DataInputStream in = new DataInputStream(raw);
3483
3484        byte[] buffer = new byte[32 * 1024];
3485        int chunkTotal;
3486        while ((chunkTotal = in.readInt()) > 0) {
3487            while (chunkTotal > 0) {
3488                int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
3489                int nRead = in.read(buffer, 0, toRead);
3490                out.write(buffer, 0, nRead);
3491                chunkTotal -= nRead;
3492            }
3493        }
3494    }
3495
3496    void tearDownAgentAndKill(ApplicationInfo app) {
3497        try {
3498            // unbind and tidy up even on timeout or failure, just in case
3499            mActivityManager.unbindBackupAgent(app);
3500
3501            // The agent was running with a stub Application object, so shut it down.
3502            // !!! We hardcode the confirmation UI's package name here rather than use a
3503            //     manifest flag!  TODO something less direct.
3504            if (app.uid >= Process.FIRST_APPLICATION_UID
3505                    && !app.packageName.equals("com.android.backupconfirm")) {
3506                if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
3507                mActivityManager.killApplicationProcess(app.processName, app.uid);
3508            } else {
3509                if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
3510            }
3511        } catch (RemoteException e) {
3512            Slog.d(TAG, "Lost app trying to shut down");
3513        }
3514    }
3515
3516    // Core logic for performing one package's full backup, gathering the tarball from the
3517    // application and emitting it to the designated OutputStream.
3518
3519    // Callout from the engine to an interested participant that might need to communicate
3520    // with the agent prior to asking it to move data
3521    interface FullBackupPreflight {
3522        /**
3523         * Perform the preflight operation necessary for the given package.
3524         * @param pkg The name of the package being proposed for full-data backup
3525         * @param agent Live BackupAgent binding to the target app's agent
3526         * @return BackupTransport.TRANSPORT_OK to proceed with the backup operation,
3527         *         or one of the other BackupTransport.* error codes as appropriate
3528         */
3529        int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
3530
3531        long getExpectedSizeOrErrorCode();
3532    };
3533
3534    class FullBackupEngine {
3535        OutputStream mOutput;
3536        FullBackupPreflight mPreflightHook;
3537        IBackupAgent mAgent;
3538        File mFilesDir;
3539        File mManifestFile;
3540        File mMetadataFile;
3541        boolean mIncludeApks;
3542        PackageInfo mPkg;
3543
3544        class FullBackupRunner implements Runnable {
3545            PackageInfo mPackage;
3546            byte[] mWidgetData;
3547            IBackupAgent mAgent;
3548            ParcelFileDescriptor mPipe;
3549            int mToken;
3550            boolean mSendApk;
3551            boolean mWriteManifest;
3552
3553            FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
3554                             int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
3555                    throws IOException {
3556                mPackage = pack;
3557                mWidgetData = widgetData;
3558                mAgent = agent;
3559                mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
3560                mToken = token;
3561                mSendApk = sendApk;
3562                mWriteManifest = writeManifest;
3563            }
3564
3565            @Override
3566            public void run() {
3567                try {
3568                    FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
3569
3570                    if (mWriteManifest) {
3571                        final boolean writeWidgetData = mWidgetData != null;
3572                        if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
3573                        writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
3574                        FullBackup.backupToTar(mPackage.packageName, null, null,
3575                                mFilesDir.getAbsolutePath(),
3576                                mManifestFile.getAbsolutePath(),
3577                                output);
3578                        mManifestFile.delete();
3579
3580                        // We only need to write a metadata file if we have widget data to stash
3581                        if (writeWidgetData) {
3582                            writeMetadata(mPackage, mMetadataFile, mWidgetData);
3583                            FullBackup.backupToTar(mPackage.packageName, null, null,
3584                                    mFilesDir.getAbsolutePath(),
3585                                    mMetadataFile.getAbsolutePath(),
3586                                    output);
3587                            mMetadataFile.delete();
3588                        }
3589                    }
3590
3591                    if (mSendApk) {
3592                        writeApkToBackup(mPackage, output);
3593                    }
3594
3595                    if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
3596                    prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null);
3597                    mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
3598                } catch (IOException e) {
3599                    Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
3600                } catch (RemoteException e) {
3601                    Slog.e(TAG, "Remote agent vanished during full backup of "
3602                            + mPackage.packageName);
3603                } finally {
3604                    try {
3605                        mPipe.close();
3606                    } catch (IOException e) {}
3607                }
3608            }
3609        }
3610
3611        FullBackupEngine(OutputStream output, FullBackupPreflight preflightHook, PackageInfo pkg,
3612                         boolean alsoApks) {
3613            mOutput = output;
3614            mPreflightHook = preflightHook;
3615            mPkg = pkg;
3616            mIncludeApks = alsoApks;
3617            mFilesDir = new File("/data/system");
3618            mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
3619            mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
3620        }
3621
3622        public int preflightCheck() throws RemoteException {
3623            if (mPreflightHook == null) {
3624                if (MORE_DEBUG) {
3625                    Slog.v(TAG, "No preflight check");
3626                }
3627                return BackupTransport.TRANSPORT_OK;
3628            }
3629            if (initializeAgent()) {
3630                int result = mPreflightHook.preflightFullBackup(mPkg, mAgent);
3631                if (MORE_DEBUG) {
3632                    Slog.v(TAG, "preflight returned " + result);
3633                }
3634                return result;
3635            } else {
3636                Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
3637                return BackupTransport.AGENT_ERROR;
3638            }
3639        }
3640
3641        public int backupOnePackage() throws RemoteException {
3642            int result = BackupTransport.AGENT_ERROR;
3643
3644            if (initializeAgent()) {
3645                ParcelFileDescriptor[] pipes = null;
3646                try {
3647                    pipes = ParcelFileDescriptor.createPipe();
3648
3649                    ApplicationInfo app = mPkg.applicationInfo;
3650                    final boolean isSharedStorage =
3651                            mPkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
3652                    final boolean sendApk = mIncludeApks
3653                            && !isSharedStorage
3654                            && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
3655                            && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
3656                            (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
3657
3658                    // TODO: http://b/22388012
3659                    byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(mPkg.packageName,
3660                            UserHandle.USER_SYSTEM);
3661
3662                    final int token = generateToken();
3663                    FullBackupRunner runner = new FullBackupRunner(mPkg, mAgent, pipes[1],
3664                            token, sendApk, !isSharedStorage, widgetBlob);
3665                    pipes[1].close();   // the runner has dup'd it
3666                    pipes[1] = null;
3667                    Thread t = new Thread(runner, "app-data-runner");
3668                    t.start();
3669
3670                    // Now pull data from the app and stuff it into the output
3671                    routeSocketDataToOutput(pipes[0], mOutput);
3672
3673                    if (!waitUntilOperationComplete(token)) {
3674                        Slog.e(TAG, "Full backup failed on package " + mPkg.packageName);
3675                    } else {
3676                        if (MORE_DEBUG) {
3677                            Slog.d(TAG, "Full package backup success: " + mPkg.packageName);
3678                        }
3679                        result = BackupTransport.TRANSPORT_OK;
3680                    }
3681                } catch (IOException e) {
3682                    Slog.e(TAG, "Error backing up " + mPkg.packageName, e);
3683                    result = BackupTransport.AGENT_ERROR;
3684                } finally {
3685                    try {
3686                        // flush after every package
3687                        mOutput.flush();
3688                        if (pipes != null) {
3689                            if (pipes[0] != null) pipes[0].close();
3690                            if (pipes[1] != null) pipes[1].close();
3691                        }
3692                    } catch (IOException e) {
3693                        Slog.w(TAG, "Error bringing down backup stack");
3694                        result = BackupTransport.TRANSPORT_ERROR;
3695                    }
3696                }
3697            } else {
3698                Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
3699            }
3700            tearDown();
3701            return result;
3702        }
3703
3704        public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
3705            if (initializeAgent()) {
3706                try {
3707                    mAgent.doQuotaExceeded(backupDataBytes, quotaBytes);
3708                } catch (RemoteException e) {
3709                    Slog.e(TAG, "Remote exception while telling agent about quota exceeded");
3710                }
3711            }
3712        }
3713
3714        private boolean initializeAgent() {
3715            if (mAgent == null) {
3716                if (MORE_DEBUG) {
3717                    Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName);
3718                }
3719                mAgent = bindToAgentSynchronous(mPkg.applicationInfo,
3720                        IApplicationThread.BACKUP_MODE_FULL);
3721            }
3722            return mAgent != null;
3723        }
3724
3725        private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
3726            // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
3727            // TODO: handle backing up split APKs
3728            final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
3729            final String apkDir = new File(appSourceDir).getParent();
3730            FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
3731                    apkDir, appSourceDir, output);
3732
3733            // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM
3734            // doesn't have access to external storage.
3735
3736            // Save associated .obb content if it exists and we did save the apk
3737            // check for .obb and save those too
3738            // TODO: http://b/22388012
3739            final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_SYSTEM);
3740            final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0];
3741            if (obbDir != null) {
3742                if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
3743                File[] obbFiles = obbDir.listFiles();
3744                if (obbFiles != null) {
3745                    final String obbDirName = obbDir.getAbsolutePath();
3746                    for (File obb : obbFiles) {
3747                        FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
3748                                obbDirName, obb.getAbsolutePath(), output);
3749                    }
3750                }
3751            }
3752        }
3753
3754        private void writeAppManifest(PackageInfo pkg, File manifestFile,
3755                boolean withApk, boolean withWidgets) throws IOException {
3756            // Manifest format. All data are strings ending in LF:
3757            //     BACKUP_MANIFEST_VERSION, currently 1
3758            //
3759            // Version 1:
3760            //     package name
3761            //     package's versionCode
3762            //     platform versionCode
3763            //     getInstallerPackageName() for this package (maybe empty)
3764            //     boolean: "1" if archive includes .apk; any other string means not
3765            //     number of signatures == N
3766            // N*:    signature byte array in ascii format per Signature.toCharsString()
3767            StringBuilder builder = new StringBuilder(4096);
3768            StringBuilderPrinter printer = new StringBuilderPrinter(builder);
3769
3770            printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
3771            printer.println(pkg.packageName);
3772            printer.println(Integer.toString(pkg.versionCode));
3773            printer.println(Integer.toString(Build.VERSION.SDK_INT));
3774
3775            String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
3776            printer.println((installerName != null) ? installerName : "");
3777
3778            printer.println(withApk ? "1" : "0");
3779            if (pkg.signatures == null) {
3780                printer.println("0");
3781            } else {
3782                printer.println(Integer.toString(pkg.signatures.length));
3783                for (Signature sig : pkg.signatures) {
3784                    printer.println(sig.toCharsString());
3785                }
3786            }
3787
3788            FileOutputStream outstream = new FileOutputStream(manifestFile);
3789            outstream.write(builder.toString().getBytes());
3790            outstream.close();
3791
3792            // We want the manifest block in the archive stream to be idempotent:
3793            // each time we generate a backup stream for the app, we want the manifest
3794            // block to be identical.  The underlying tar mechanism sees it as a file,
3795            // though, and will propagate its mtime, causing the tar header to vary.
3796            // Avoid this problem by pinning the mtime to zero.
3797            manifestFile.setLastModified(0);
3798        }
3799
3800        // Widget metadata format. All header entries are strings ending in LF:
3801        //
3802        // Version 1 header:
3803        //     BACKUP_METADATA_VERSION, currently "1"
3804        //     package name
3805        //
3806        // File data (all integers are binary in network byte order)
3807        // *N: 4 : integer token identifying which metadata blob
3808        //     4 : integer size of this blob = N
3809        //     N : raw bytes of this metadata blob
3810        //
3811        // Currently understood blobs (always in network byte order):
3812        //
3813        //     widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
3814        //
3815        // Unrecognized blobs are *ignored*, not errors.
3816        private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
3817                throws IOException {
3818            StringBuilder b = new StringBuilder(512);
3819            StringBuilderPrinter printer = new StringBuilderPrinter(b);
3820            printer.println(Integer.toString(BACKUP_METADATA_VERSION));
3821            printer.println(pkg.packageName);
3822
3823            FileOutputStream fout = new FileOutputStream(destination);
3824            BufferedOutputStream bout = new BufferedOutputStream(fout);
3825            DataOutputStream out = new DataOutputStream(bout);
3826            bout.write(b.toString().getBytes());    // bypassing DataOutputStream
3827
3828            if (widgetData != null && widgetData.length > 0) {
3829                out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
3830                out.writeInt(widgetData.length);
3831                out.write(widgetData);
3832            }
3833            bout.flush();
3834            out.close();
3835
3836            // As with the manifest file, guarantee idempotence of the archive metadata
3837            // for the widget block by using a fixed mtime on the transient file.
3838            destination.setLastModified(0);
3839        }
3840
3841        private void tearDown() {
3842            if (mPkg != null) {
3843                final ApplicationInfo app = mPkg.applicationInfo;
3844                if (app != null) {
3845                    tearDownAgentAndKill(app);
3846                }
3847            }
3848        }
3849    }
3850
3851    // Generic driver skeleton for full backup operations
3852    abstract class FullBackupTask implements Runnable {
3853        IFullBackupRestoreObserver mObserver;
3854
3855        FullBackupTask(IFullBackupRestoreObserver observer) {
3856            mObserver = observer;
3857        }
3858
3859        // wrappers for observer use
3860        final void sendStartBackup() {
3861            if (mObserver != null) {
3862                try {
3863                    mObserver.onStartBackup();
3864                } catch (RemoteException e) {
3865                    Slog.w(TAG, "full backup observer went away: startBackup");
3866                    mObserver = null;
3867                }
3868            }
3869        }
3870
3871        final void sendOnBackupPackage(String name) {
3872            if (mObserver != null) {
3873                try {
3874                    // TODO: use a more user-friendly name string
3875                    mObserver.onBackupPackage(name);
3876                } catch (RemoteException e) {
3877                    Slog.w(TAG, "full backup observer went away: backupPackage");
3878                    mObserver = null;
3879                }
3880            }
3881        }
3882
3883        final void sendEndBackup() {
3884            if (mObserver != null) {
3885                try {
3886                    mObserver.onEndBackup();
3887                } catch (RemoteException e) {
3888                    Slog.w(TAG, "full backup observer went away: endBackup");
3889                    mObserver = null;
3890                }
3891            }
3892        }
3893    }
3894
3895    boolean deviceIsEncrypted() {
3896        try {
3897            return mMountService.getEncryptionState()
3898                     != IMountService.ENCRYPTION_STATE_NONE
3899                && mMountService.getPasswordType()
3900                     != StorageManager.CRYPT_TYPE_DEFAULT;
3901        } catch (Exception e) {
3902            // If we can't talk to the mount service we have a serious problem; fail
3903            // "secure" i.e. assuming that the device is encrypted.
3904            Slog.e(TAG, "Unable to communicate with mount service: " + e.getMessage());
3905            return true;
3906        }
3907    }
3908
3909    // Full backup task variant used for adb backup
3910    class PerformAdbBackupTask extends FullBackupTask {
3911        FullBackupEngine mBackupEngine;
3912        final AtomicBoolean mLatch;
3913
3914        ParcelFileDescriptor mOutputFile;
3915        DeflaterOutputStream mDeflater;
3916        boolean mIncludeApks;
3917        boolean mIncludeObbs;
3918        boolean mIncludeShared;
3919        boolean mDoWidgets;
3920        boolean mAllApps;
3921        boolean mIncludeSystem;
3922        boolean mCompress;
3923        ArrayList<String> mPackages;
3924        String mCurrentPassword;
3925        String mEncryptPassword;
3926
3927        PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
3928                boolean includeApks, boolean includeObbs, boolean includeShared,
3929                boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
3930                boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch) {
3931            super(observer);
3932            mLatch = latch;
3933
3934            mOutputFile = fd;
3935            mIncludeApks = includeApks;
3936            mIncludeObbs = includeObbs;
3937            mIncludeShared = includeShared;
3938            mDoWidgets = doWidgets;
3939            mAllApps = doAllApps;
3940            mIncludeSystem = doSystem;
3941            mPackages = (packages == null)
3942                    ? new ArrayList<String>()
3943                    : new ArrayList<String>(Arrays.asList(packages));
3944            mCurrentPassword = curPassword;
3945            // when backing up, if there is a current backup password, we require that
3946            // the user use a nonempty encryption password as well.  if one is supplied
3947            // in the UI we use that, but if the UI was left empty we fall back to the
3948            // current backup password (which was supplied by the user as well).
3949            if (encryptPassword == null || "".equals(encryptPassword)) {
3950                mEncryptPassword = curPassword;
3951            } else {
3952                mEncryptPassword = encryptPassword;
3953            }
3954            mCompress = doCompress;
3955        }
3956
3957        void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
3958            for (String pkgName : pkgNames) {
3959                if (!set.containsKey(pkgName)) {
3960                    try {
3961                        PackageInfo info = mPackageManager.getPackageInfo(pkgName,
3962                                PackageManager.GET_SIGNATURES);
3963                        set.put(pkgName, info);
3964                    } catch (NameNotFoundException e) {
3965                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
3966                    }
3967                }
3968            }
3969        }
3970
3971        private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
3972                OutputStream ofstream) throws Exception {
3973            // User key will be used to encrypt the master key.
3974            byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE);
3975            SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt,
3976                    PBKDF2_HASH_ROUNDS);
3977
3978            // the master key is random for each backup
3979            byte[] masterPw = new byte[256 / 8];
3980            mRng.nextBytes(masterPw);
3981            byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE);
3982
3983            // primary encryption of the datastream with the random key
3984            Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
3985            SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES");
3986            c.init(Cipher.ENCRYPT_MODE, masterKeySpec);
3987            OutputStream finalOutput = new CipherOutputStream(ofstream, c);
3988
3989            // line 4: name of encryption algorithm
3990            headerbuf.append(ENCRYPTION_ALGORITHM_NAME);
3991            headerbuf.append('\n');
3992            // line 5: user password salt [hex]
3993            headerbuf.append(byteArrayToHex(newUserSalt));
3994            headerbuf.append('\n');
3995            // line 6: master key checksum salt [hex]
3996            headerbuf.append(byteArrayToHex(checksumSalt));
3997            headerbuf.append('\n');
3998            // line 7: number of PBKDF2 rounds used [decimal]
3999            headerbuf.append(PBKDF2_HASH_ROUNDS);
4000            headerbuf.append('\n');
4001
4002            // line 8: IV of the user key [hex]
4003            Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding");
4004            mkC.init(Cipher.ENCRYPT_MODE, userKey);
4005
4006            byte[] IV = mkC.getIV();
4007            headerbuf.append(byteArrayToHex(IV));
4008            headerbuf.append('\n');
4009
4010            // line 9: master IV + key blob, encrypted by the user key [hex].  Blob format:
4011            //    [byte] IV length = Niv
4012            //    [array of Niv bytes] IV itself
4013            //    [byte] master key length = Nmk
4014            //    [array of Nmk bytes] master key itself
4015            //    [byte] MK checksum hash length = Nck
4016            //    [array of Nck bytes] master key checksum hash
4017            //
4018            // The checksum is the (master key + checksum salt), run through the
4019            // stated number of PBKDF2 rounds
4020            IV = c.getIV();
4021            byte[] mk = masterKeySpec.getEncoded();
4022            byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(),
4023                    checksumSalt, PBKDF2_HASH_ROUNDS);
4024
4025            ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length
4026                    + checksum.length + 3);
4027            DataOutputStream mkOut = new DataOutputStream(blob);
4028            mkOut.writeByte(IV.length);
4029            mkOut.write(IV);
4030            mkOut.writeByte(mk.length);
4031            mkOut.write(mk);
4032            mkOut.writeByte(checksum.length);
4033            mkOut.write(checksum);
4034            mkOut.flush();
4035            byte[] encryptedMk = mkC.doFinal(blob.toByteArray());
4036            headerbuf.append(byteArrayToHex(encryptedMk));
4037            headerbuf.append('\n');
4038
4039            return finalOutput;
4040        }
4041
4042        private void finalizeBackup(OutputStream out) {
4043            try {
4044                // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes.
4045                byte[] eof = new byte[512 * 2]; // newly allocated == zero filled
4046                out.write(eof);
4047            } catch (IOException e) {
4048                Slog.w(TAG, "Error attempting to finalize backup stream");
4049            }
4050        }
4051
4052        @Override
4053        public void run() {
4054            Slog.i(TAG, "--- Performing full-dataset adb backup ---");
4055
4056            TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
4057            FullBackupObbConnection obbConnection = new FullBackupObbConnection();
4058            obbConnection.establish();  // we'll want this later
4059
4060            sendStartBackup();
4061
4062            // doAllApps supersedes the package set if any
4063            if (mAllApps) {
4064                List<PackageInfo> allPackages = mPackageManager.getInstalledPackages(
4065                        PackageManager.GET_SIGNATURES);
4066                for (int i = 0; i < allPackages.size(); i++) {
4067                    PackageInfo pkg = allPackages.get(i);
4068                    // Exclude system apps if we've been asked to do so
4069                    if (mIncludeSystem == true
4070                            || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
4071                        packagesToBackup.put(pkg.packageName, pkg);
4072                    }
4073                }
4074            }
4075
4076            // If we're doing widget state as well, ensure that we have all the involved
4077            // host & provider packages in the set
4078            if (mDoWidgets) {
4079                // TODO: http://b/22388012
4080                List<String> pkgs =
4081                        AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_SYSTEM);
4082                if (pkgs != null) {
4083                    if (MORE_DEBUG) {
4084                        Slog.i(TAG, "Adding widget participants to backup set:");
4085                        StringBuilder sb = new StringBuilder(128);
4086                        sb.append("   ");
4087                        for (String s : pkgs) {
4088                            sb.append(' ');
4089                            sb.append(s);
4090                        }
4091                        Slog.i(TAG, sb.toString());
4092                    }
4093                    addPackagesToSet(packagesToBackup, pkgs);
4094                }
4095            }
4096
4097            // Now process the command line argument packages, if any. Note that explicitly-
4098            // named system-partition packages will be included even if includeSystem was
4099            // set to false.
4100            if (mPackages != null) {
4101                addPackagesToSet(packagesToBackup, mPackages);
4102            }
4103
4104            // Now we cull any inapplicable / inappropriate packages from the set.  This
4105            // includes the special shared-storage agent package; we handle that one
4106            // explicitly at the end of the backup pass.
4107            Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
4108            while (iter.hasNext()) {
4109                PackageInfo pkg = iter.next().getValue();
4110                if (!appIsEligibleForBackup(pkg.applicationInfo)) {
4111                    iter.remove();
4112                }
4113            }
4114
4115            // flatten the set of packages now so we can explicitly control the ordering
4116            ArrayList<PackageInfo> backupQueue =
4117                    new ArrayList<PackageInfo>(packagesToBackup.values());
4118            FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
4119            OutputStream out = null;
4120
4121            PackageInfo pkg = null;
4122            try {
4123                boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
4124
4125                // Only allow encrypted backups of encrypted devices
4126                if (deviceIsEncrypted() && !encrypting) {
4127                    Slog.e(TAG, "Unencrypted backup of encrypted device; aborting");
4128                    return;
4129                }
4130
4131                OutputStream finalOutput = ofstream;
4132
4133                // Verify that the given password matches the currently-active
4134                // backup password, if any
4135                if (!backupPasswordMatches(mCurrentPassword)) {
4136                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
4137                    return;
4138                }
4139
4140                // Write the global file header.  All strings are UTF-8 encoded; lines end
4141                // with a '\n' byte.  Actual backup data begins immediately following the
4142                // final '\n'.
4143                //
4144                // line 1: "ANDROID BACKUP"
4145                // line 2: backup file format version, currently "2"
4146                // line 3: compressed?  "0" if not compressed, "1" if compressed.
4147                // line 4: name of encryption algorithm [currently only "none" or "AES-256"]
4148                //
4149                // When line 4 is not "none", then additional header data follows:
4150                //
4151                // line 5: user password salt [hex]
4152                // line 6: master key checksum salt [hex]
4153                // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal]
4154                // line 8: IV of the user key [hex]
4155                // line 9: master key blob [hex]
4156                //     IV of the master key, master key itself, master key checksum hash
4157                //
4158                // The master key checksum is the master key plus its checksum salt, run through
4159                // 10k rounds of PBKDF2.  This is used to verify that the user has supplied the
4160                // correct password for decrypting the archive:  the master key decrypted from
4161                // the archive using the user-supplied password is also run through PBKDF2 in
4162                // this way, and if the result does not match the checksum as stored in the
4163                // archive, then we know that the user-supplied password does not match the
4164                // archive's.
4165                StringBuilder headerbuf = new StringBuilder(1024);
4166
4167                headerbuf.append(BACKUP_FILE_HEADER_MAGIC);
4168                headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n
4169                headerbuf.append(mCompress ? "\n1\n" : "\n0\n");
4170
4171                try {
4172                    // Set up the encryption stage if appropriate, and emit the correct header
4173                    if (encrypting) {
4174                        finalOutput = emitAesBackupHeader(headerbuf, finalOutput);
4175                    } else {
4176                        headerbuf.append("none\n");
4177                    }
4178
4179                    byte[] header = headerbuf.toString().getBytes("UTF-8");
4180                    ofstream.write(header);
4181
4182                    // Set up the compression stage feeding into the encryption stage (if any)
4183                    if (mCompress) {
4184                        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
4185                        finalOutput = new DeflaterOutputStream(finalOutput, deflater, true);
4186                    }
4187
4188                    out = finalOutput;
4189                } catch (Exception e) {
4190                    // Should never happen!
4191                    Slog.e(TAG, "Unable to emit archive header", e);
4192                    return;
4193                }
4194
4195                // Shared storage if requested
4196                if (mIncludeShared) {
4197                    try {
4198                        pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
4199                        backupQueue.add(pkg);
4200                    } catch (NameNotFoundException e) {
4201                        Slog.e(TAG, "Unable to find shared-storage backup handler");
4202                    }
4203                }
4204
4205                // Now actually run the constructed backup sequence
4206                int N = backupQueue.size();
4207                for (int i = 0; i < N; i++) {
4208                    pkg = backupQueue.get(i);
4209                    final boolean isSharedStorage =
4210                            pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
4211
4212                    mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks);
4213                    sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
4214                    // Don't need to check preflight result as there is no preflight hook.
4215                    mBackupEngine.backupOnePackage();
4216
4217                    // after the app's agent runs to handle its private filesystem
4218                    // contents, back up any OBB content it has on its behalf.
4219                    if (mIncludeObbs) {
4220                        boolean obbOkay = obbConnection.backupObbs(pkg, out);
4221                        if (!obbOkay) {
4222                            throw new RuntimeException("Failure writing OBB stack for " + pkg);
4223                        }
4224                    }
4225                }
4226
4227                // Done!
4228                finalizeBackup(out);
4229            } catch (RemoteException e) {
4230                Slog.e(TAG, "App died during full backup");
4231            } catch (Exception e) {
4232                Slog.e(TAG, "Internal exception during full backup", e);
4233            } finally {
4234                try {
4235                    if (out != null) out.close();
4236                    mOutputFile.close();
4237                } catch (IOException e) {
4238                    /* nothing we can do about this */
4239                }
4240                synchronized (mCurrentOpLock) {
4241                    mCurrentOperations.clear();
4242                }
4243                synchronized (mLatch) {
4244                    mLatch.set(true);
4245                    mLatch.notifyAll();
4246                }
4247                sendEndBackup();
4248                obbConnection.tearDown();
4249                if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
4250                mWakelock.release();
4251            }
4252        }
4253    }
4254
4255    // Full backup task extension used for transport-oriented operation
4256    class PerformFullTransportBackupTask extends FullBackupTask {
4257        static final String TAG = "PFTBT";
4258        ArrayList<PackageInfo> mPackages;
4259        boolean mUpdateSchedule;
4260        CountDownLatch mLatch;
4261        AtomicBoolean mKeepRunning;     // signal from job scheduler
4262        FullBackupJob mJob;             // if a scheduled job needs to be finished afterwards
4263        IBackupObserver mBackupObserver;
4264        boolean mUserInitiated;
4265
4266        PerformFullTransportBackupTask(IFullBackupRestoreObserver observer,
4267                String[] whichPackages, boolean updateSchedule,
4268                FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver,
4269                boolean userInitiated) {
4270            super(observer);
4271            mUpdateSchedule = updateSchedule;
4272            mLatch = latch;
4273            mKeepRunning = new AtomicBoolean(true);
4274            mJob = runningJob;
4275            mPackages = new ArrayList<PackageInfo>(whichPackages.length);
4276            mBackupObserver = backupObserver;
4277            mUserInitiated = userInitiated;
4278
4279            for (String pkg : whichPackages) {
4280                try {
4281                    PackageInfo info = mPackageManager.getPackageInfo(pkg,
4282                            PackageManager.GET_SIGNATURES);
4283                    if (!appIsEligibleForBackup(info.applicationInfo)) {
4284                        // Cull any packages that have indicated that backups are not permitted,
4285                        // that run as system-domain uids but do not define their own backup agents,
4286                        // as well as any explicit mention of the 'special' shared-storage agent
4287                        // package (we handle that one at the end).
4288                        if (MORE_DEBUG) {
4289                            Slog.d(TAG, "Ignoring ineligible package " + pkg);
4290                        }
4291                        sendBackupOnPackageResult(mBackupObserver, pkg,
4292                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4293                        continue;
4294                    } else if (!appGetsFullBackup(info)) {
4295                        // Cull any packages that are found in the queue but now aren't supposed
4296                        // to get full-data backup operations.
4297                        if (MORE_DEBUG) {
4298                            Slog.d(TAG, "Ignoring full-data backup of key/value participant "
4299                                    + pkg);
4300                        }
4301                        sendBackupOnPackageResult(mBackupObserver, pkg,
4302                                BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4303                        continue;
4304                    } else if (appIsStopped(info.applicationInfo)) {
4305                        // Cull any packages in the 'stopped' state: they've either just been
4306                        // installed or have explicitly been force-stopped by the user.  In both
4307                        // cases we do not want to launch them for backup.
4308                        if (MORE_DEBUG) {
4309                            Slog.d(TAG, "Ignoring stopped package " + pkg);
4310                        }
4311                        sendBackupOnPackageResult(mBackupObserver, pkg,
4312                                BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4313                        continue;
4314                    }
4315                    mPackages.add(info);
4316                } catch (NameNotFoundException e) {
4317                    Slog.i(TAG, "Requested package " + pkg + " not found; ignoring");
4318                }
4319            }
4320        }
4321
4322        public void setRunning(boolean running) {
4323            mKeepRunning.set(running);
4324        }
4325
4326        @Override
4327        public void run() {
4328            // data from the app, passed to us for bridging to the transport
4329            ParcelFileDescriptor[] enginePipes = null;
4330
4331            // Pipe through which we write data to the transport
4332            ParcelFileDescriptor[] transportPipes = null;
4333
4334            long backoff = 0;
4335            int backupRunStatus = BackupManager.SUCCESS;
4336
4337            try {
4338                if (!mEnabled || !mProvisioned) {
4339                    // Backups are globally disabled, so don't proceed.
4340                    if (DEBUG) {
4341                        Slog.i(TAG, "full backup requested but e=" + mEnabled
4342                                + " p=" + mProvisioned + "; ignoring");
4343                    }
4344                    mUpdateSchedule = false;
4345                    backupRunStatus = BackupManager.ERROR_BACKUP_NOT_ALLOWED;
4346                    return;
4347                }
4348
4349                IBackupTransport transport = getTransport(mCurrentTransport);
4350                if (transport == null) {
4351                    Slog.w(TAG, "Transport not present; full data backup not performed");
4352                    backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
4353                    return;
4354                }
4355
4356                // Set up to send data to the transport
4357                final int N = mPackages.size();
4358                final byte[] buffer = new byte[8192];
4359                for (int i = 0; i < N; i++) {
4360                    PackageInfo currentPackage = mPackages.get(i);
4361                    String packageName = currentPackage.packageName;
4362                    if (DEBUG) {
4363                        Slog.i(TAG, "Initiating full-data transport backup of " + packageName);
4364                    }
4365                    EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, packageName);
4366
4367                    transportPipes = ParcelFileDescriptor.createPipe();
4368
4369                    // Tell the transport the data's coming
4370                    int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
4371                    int backupPackageStatus = transport.performFullBackup(currentPackage,
4372                            transportPipes[0], flags);
4373                    if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
4374                        // The transport has its own copy of the read end of the pipe,
4375                        // so close ours now
4376                        transportPipes[0].close();
4377                        transportPipes[0] = null;
4378
4379                        // Now set up the backup engine / data source end of things
4380                        enginePipes = ParcelFileDescriptor.createPipe();
4381                        SinglePackageBackupRunner backupRunner =
4382                                new SinglePackageBackupRunner(enginePipes[1], currentPackage,
4383                                        transport);
4384                        // The runner dup'd the pipe half, so we close it here
4385                        enginePipes[1].close();
4386                        enginePipes[1] = null;
4387
4388                        // Spin off the runner to fetch the app's data and pipe it
4389                        // into the engine pipes
4390                        (new Thread(backupRunner, "package-backup-bridge")).start();
4391
4392                        // Read data off the engine pipe and pass it to the transport
4393                        // pipe until we hit EOD on the input stream.
4394                        FileInputStream in = new FileInputStream(
4395                                enginePipes[0].getFileDescriptor());
4396                        FileOutputStream out = new FileOutputStream(
4397                                transportPipes[1].getFileDescriptor());
4398                        long totalRead = 0;
4399                        final long preflightResult = backupRunner.getPreflightResultBlocking();
4400                        // Preflight result is negative if some error happened on preflight.
4401                        if (preflightResult < 0) {
4402                            if (MORE_DEBUG) {
4403                                Slog.d(TAG, "Backup error after preflight of package "
4404                                        + packageName + ": " + preflightResult
4405                                        + ", not running backup.");
4406                            }
4407                            backupPackageStatus = (int) preflightResult;
4408                        } else {
4409                            int nRead = 0;
4410                            do {
4411                                if (!mKeepRunning.get()) {
4412                                    if (DEBUG_SCHEDULING) {
4413                                        Slog.i(TAG, "Full backup task told to stop");
4414                                    }
4415                                    break;
4416                                }
4417                                nRead = in.read(buffer);
4418                                if (MORE_DEBUG) {
4419                                    Slog.v(TAG, "in.read(buffer) from app: " + nRead);
4420                                }
4421                                if (nRead > 0) {
4422                                    out.write(buffer, 0, nRead);
4423                                    backupPackageStatus = transport.sendBackupData(nRead);
4424                                    totalRead += nRead;
4425                                    if (mBackupObserver != null && preflightResult > 0) {
4426                                        sendBackupOnUpdate(mBackupObserver, packageName,
4427                                                new BackupProgress(preflightResult, totalRead));
4428                                    }
4429                                }
4430                            } while (nRead > 0
4431                                    && backupPackageStatus == BackupTransport.TRANSPORT_OK);
4432
4433                            // Despite preflight succeeded, package still can hit quota on flight.
4434                            if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
4435                                long quota = transport.getBackupQuota(packageName, true);
4436                                Slog.w(TAG, "Package hit quota limit in-flight " + packageName
4437                                        + ": " + totalRead + " of " + quota);
4438                                backupRunner.sendQuotaExceeded(totalRead, quota);
4439                            }
4440                        }
4441
4442
4443                        // If we've lost our running criteria, tell the transport to cancel
4444                        // and roll back this (partial) backup payload; otherwise tell it
4445                        // that we've reached the clean finish state.
4446                        if (!mKeepRunning.get()) {
4447                            backupPackageStatus = BackupTransport.TRANSPORT_ERROR;
4448                            transport.cancelFullBackup();
4449                        } else {
4450                            // If we were otherwise in a good state, now interpret the final
4451                            // result based on what finishBackup() returns.  If we're in a
4452                            // failure case already, preserve that result and ignore whatever
4453                            // finishBackup() reports.
4454                            final int finishResult = transport.finishBackup();
4455                            if (backupPackageStatus == BackupTransport.TRANSPORT_OK) {
4456                                backupPackageStatus = finishResult;
4457                            }
4458                        }
4459
4460                        // TRANSPORT_ERROR here means that we've hit an error that the runner
4461                        // doesn't know about, so it's still moving data but we're pulling the
4462                        // rug out from under it.  Don't ask for its result:  we already know better
4463                        // and we'll hang if we block waiting for it, since it relies on us to
4464                        // read back the data it's writing into the engine.  Just proceed with
4465                        // a graceful failure.  The runner/engine mechanism will tear itself
4466                        // down cleanly when we close the pipes from this end.
4467                        if (backupPackageStatus != BackupTransport.TRANSPORT_ERROR) {
4468                            // We still could fail in backup runner thread, getting result from there.
4469                            int backupRunnerResult = backupRunner.getBackupResultBlocking();
4470                            if (backupRunnerResult != BackupTransport.TRANSPORT_OK) {
4471                                // If there was an error in runner thread and
4472                                // not TRANSPORT_ERROR here, overwrite it.
4473                                backupPackageStatus = backupRunnerResult;
4474                            }
4475                        }
4476
4477                        if (MORE_DEBUG) {
4478                            Slog.i(TAG, "Done trying to send backup data: result="
4479                                    + backupPackageStatus);
4480                        }
4481
4482                        if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
4483                            Slog.e(TAG, "Error " + backupPackageStatus + " backing up "
4484                                    + packageName);
4485                        }
4486
4487                        // Also ask the transport how long it wants us to wait before
4488                        // moving on to the next package, if any.
4489                        backoff = transport.requestFullBackupTime();
4490                        if (DEBUG_SCHEDULING) {
4491                            Slog.i(TAG, "Transport suggested backoff=" + backoff);
4492                        }
4493
4494                    }
4495
4496                    // Roll this package to the end of the backup queue if we're
4497                    // in a queue-driven mode (regardless of success/failure)
4498                    if (mUpdateSchedule) {
4499                        enqueueFullBackup(packageName, System.currentTimeMillis());
4500                    }
4501
4502                    if (backupPackageStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
4503                        sendBackupOnPackageResult(mBackupObserver, packageName,
4504                                BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
4505                        if (DEBUG) {
4506                            Slog.i(TAG, "Transport rejected backup of " + packageName
4507                                    + ", skipping");
4508                        }
4509                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName,
4510                                "transport rejected");
4511                        // Do nothing, clean up, and continue looping.
4512                    } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
4513                        sendBackupOnPackageResult(mBackupObserver, packageName,
4514                                BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
4515                        if (DEBUG) {
4516                            Slog.i(TAG, "Transport quota exceeded for package: " + packageName);
4517                            EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
4518                                    packageName);
4519                        }
4520                        // Do nothing, clean up, and continue looping.
4521                    } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
4522                        sendBackupOnPackageResult(mBackupObserver, packageName,
4523                                BackupManager.ERROR_AGENT_FAILURE);
4524                        Slog.w(TAG, "Application failure for package: " + packageName);
4525                        EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
4526                        // Do nothing, clean up, and continue looping.
4527                    } else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
4528                        sendBackupOnPackageResult(mBackupObserver, packageName,
4529                            BackupManager.ERROR_TRANSPORT_ABORTED);
4530                        Slog.w(TAG, "Transport failed; aborting backup: " + backupPackageStatus);
4531                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
4532                        // Abort entire backup pass.
4533                        backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
4534                        return;
4535                    } else {
4536                        // Success!
4537                        sendBackupOnPackageResult(mBackupObserver, packageName,
4538                                BackupManager.SUCCESS);
4539                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, packageName);
4540                        logBackupComplete(packageName);
4541                    }
4542                    cleanUpPipes(transportPipes);
4543                    cleanUpPipes(enginePipes);
4544                }
4545            } catch (Exception e) {
4546                backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
4547                Slog.w(TAG, "Exception trying full transport backup", e);
4548            } finally {
4549                if (DEBUG) {
4550                    Slog.i(TAG, "Full backup completed with status: " + backupRunStatus);
4551                }
4552                sendBackupFinished(mBackupObserver, backupRunStatus);
4553
4554                cleanUpPipes(transportPipes);
4555                cleanUpPipes(enginePipes);
4556
4557                if (mJob != null) {
4558                    mJob.finishBackupPass();
4559                }
4560
4561                synchronized (mQueueLock) {
4562                    mRunningFullBackupTask = null;
4563                }
4564
4565
4566                mLatch.countDown();
4567
4568                // Now that we're actually done with schedule-driven work, reschedule
4569                // the next pass based on the new queue state.
4570                if (mUpdateSchedule) {
4571                    scheduleNextFullBackupJob(backoff);
4572                }
4573                Slog.i(BackupManagerService.TAG, "Full data backup pass finished.");
4574                mWakelock.release();
4575            }
4576        }
4577
4578        void cleanUpPipes(ParcelFileDescriptor[] pipes) {
4579            if (pipes != null) {
4580                if (pipes[0] != null) {
4581                    ParcelFileDescriptor fd = pipes[0];
4582                    pipes[0] = null;
4583                    try {
4584                        fd.close();
4585                    } catch (IOException e) {
4586                        Slog.w(TAG, "Unable to close pipe!");
4587                    }
4588                }
4589                if (pipes[1] != null) {
4590                    ParcelFileDescriptor fd = pipes[1];
4591                    pipes[1] = null;
4592                    try {
4593                        fd.close();
4594                    } catch (IOException e) {
4595                        Slog.w(TAG, "Unable to close pipe!");
4596                    }
4597                }
4598            }
4599        }
4600
4601        // Run the backup and pipe it back to the given socket -- expects to run on
4602        // a standalone thread.  The  runner owns this half of the pipe, and closes
4603        // it to indicate EOD to the other end.
4604        class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
4605            final AtomicLong mResult = new AtomicLong();
4606            final CountDownLatch mLatch = new CountDownLatch(1);
4607            final IBackupTransport mTransport;
4608
4609            public SinglePackageBackupPreflight(IBackupTransport transport) {
4610                mTransport = transport;
4611            }
4612
4613            @Override
4614            public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
4615                int result;
4616                try {
4617                    final int token = generateToken();
4618                    prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this);
4619                    addBackupTrace("preflighting");
4620                    if (MORE_DEBUG) {
4621                        Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
4622                    }
4623                    agent.doMeasureFullBackup(token, mBackupManagerBinder);
4624
4625                    // now wait to get our result back
4626                    mLatch.await();
4627                    long totalSize = mResult.get();
4628                    // If preflight timeouted, mResult will contain error code as int.
4629                    if (totalSize < 0) {
4630                        return (int) totalSize;
4631                    }
4632                    if (MORE_DEBUG) {
4633                        Slog.v(TAG, "Got preflight response; size=" + totalSize);
4634                    }
4635
4636                    result = mTransport.checkFullBackupSize(totalSize);
4637                    if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
4638                        final long quota = mTransport.getBackupQuota(pkg.packageName, true);
4639                        if (MORE_DEBUG) {
4640                            Slog.d(TAG, "Package hit quota limit on preflight " +
4641                                    pkg.packageName + ": " + totalSize + " of " + quota);
4642                        }
4643                        agent.doQuotaExceeded(totalSize, quota);
4644                    }
4645                } catch (Exception e) {
4646                    Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
4647                    result = BackupTransport.AGENT_ERROR;
4648                }
4649                return result;
4650            }
4651
4652            @Override
4653            public void execute() {
4654                // Unused in this case
4655            }
4656
4657            @Override
4658            public void operationComplete(long result) {
4659                // got the callback, and our preflightFullBackup() method is waiting for the result
4660                if (MORE_DEBUG) {
4661                    Slog.i(TAG, "Preflight op complete, result=" + result);
4662                }
4663                mResult.set(result);
4664                mLatch.countDown();
4665            }
4666
4667            @Override
4668            public void handleTimeout() {
4669                if (MORE_DEBUG) {
4670                    Slog.i(TAG, "Preflight timeout; failing");
4671                }
4672                mResult.set(BackupTransport.AGENT_ERROR);
4673                mLatch.countDown();
4674            }
4675
4676            @Override
4677            public long getExpectedSizeOrErrorCode() {
4678                try {
4679                    mLatch.await();
4680                    return mResult.get();
4681                } catch (InterruptedException e) {
4682                    return BackupTransport.NO_MORE_DATA;
4683                }
4684            }
4685        }
4686
4687        class SinglePackageBackupRunner implements Runnable {
4688            final ParcelFileDescriptor mOutput;
4689            final PackageInfo mTarget;
4690            final FullBackupPreflight mPreflight;
4691            final CountDownLatch mPreflightLatch;
4692            final CountDownLatch mBackupLatch;
4693            private FullBackupEngine mEngine;
4694            private volatile int mPreflightResult;
4695            private volatile int mBackupResult;
4696
4697            SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
4698                    IBackupTransport transport) throws IOException {
4699                mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
4700                mTarget = target;
4701                mPreflight = new SinglePackageBackupPreflight(transport);
4702                mPreflightLatch = new CountDownLatch(1);
4703                mBackupLatch = new CountDownLatch(1);
4704                mPreflightResult = BackupTransport.TRANSPORT_OK;
4705                mBackupResult = BackupTransport.TRANSPORT_OK;
4706            }
4707
4708            @Override
4709            public void run() {
4710                FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
4711                mEngine = new FullBackupEngine(out, mPreflight, mTarget, false);
4712                try {
4713                    try {
4714                        mPreflightResult = mEngine.preflightCheck();
4715                    } finally {
4716                        mPreflightLatch.countDown();
4717                    }
4718                    // If there is no error on preflight, continue backup.
4719                    if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
4720                        mBackupResult = mEngine.backupOnePackage();
4721                    }
4722                } catch (Exception e) {
4723                    Slog.e(TAG, "Exception during full package backup of " + mTarget.packageName);
4724                } finally {
4725                    mBackupLatch.countDown();
4726                    try {
4727                        mOutput.close();
4728                    } catch (IOException e) {
4729                        Slog.w(TAG, "Error closing transport pipe in runner");
4730                    }
4731                }
4732            }
4733
4734            public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) {
4735                mEngine.sendQuotaExceeded(backupDataBytes, quotaBytes);
4736            }
4737
4738            // If preflight succeeded, returns positive number - preflight size,
4739            // otherwise return negative error code.
4740            long getPreflightResultBlocking() {
4741                try {
4742                    mPreflightLatch.await();
4743                    if (mPreflightResult == BackupTransport.TRANSPORT_OK) {
4744                        return mPreflight.getExpectedSizeOrErrorCode();
4745                    } else {
4746                        return mPreflightResult;
4747                    }
4748                } catch (InterruptedException e) {
4749                    return BackupTransport.AGENT_ERROR;
4750                }
4751            }
4752
4753            int getBackupResultBlocking() {
4754                try {
4755                    mBackupLatch.await();
4756                    return mBackupResult;
4757                } catch (InterruptedException e) {
4758                    return BackupTransport.AGENT_ERROR;
4759                }
4760            }
4761        }
4762    }
4763
4764    // ----- Full-data backup scheduling -----
4765
4766    /**
4767     * Schedule a job to tell us when it's a good time to run a full backup
4768     */
4769    void scheduleNextFullBackupJob(long transportMinLatency) {
4770        synchronized (mQueueLock) {
4771            if (mFullBackupQueue.size() > 0) {
4772                // schedule the next job at the point in the future when the least-recently
4773                // backed up app comes due for backup again; or immediately if it's already
4774                // due.
4775                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
4776                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
4777                final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
4778                        ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
4779                final long latency = Math.max(transportMinLatency, appLatency);
4780                Runnable r = new Runnable() {
4781                    @Override public void run() {
4782                        FullBackupJob.schedule(mContext, latency);
4783                    }
4784                };
4785                mBackupHandler.postDelayed(r, 2500);
4786            } else {
4787                if (DEBUG_SCHEDULING) {
4788                    Slog.i(TAG, "Full backup queue empty; not scheduling");
4789                }
4790            }
4791        }
4792    }
4793
4794    /**
4795     * Remove a package from the full-data queue.
4796     */
4797    void dequeueFullBackupLocked(String packageName) {
4798        final int N = mFullBackupQueue.size();
4799        for (int i = N-1; i >= 0; i--) {
4800            final FullBackupEntry e = mFullBackupQueue.get(i);
4801            if (packageName.equals(e.packageName)) {
4802                mFullBackupQueue.remove(i);
4803            }
4804        }
4805    }
4806
4807    /**
4808     * Enqueue full backup for the given app, with a note about when it last ran.
4809     */
4810    void enqueueFullBackup(String packageName, long lastBackedUp) {
4811        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
4812        synchronized (mQueueLock) {
4813            // First, sanity check that we aren't adding a duplicate.  Slow but
4814            // straightforward; we'll have at most on the order of a few hundred
4815            // items in this list.
4816            dequeueFullBackupLocked(packageName);
4817
4818            // This is also slow but easy for modest numbers of apps: work backwards
4819            // from the end of the queue until we find an item whose last backup
4820            // time was before this one, then insert this new entry after it.  If we're
4821            // adding something new we don't bother scanning, and just prepend.
4822            int which = -1;
4823            if (lastBackedUp > 0) {
4824                for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
4825                    final FullBackupEntry entry = mFullBackupQueue.get(which);
4826                    if (entry.lastBackup <= lastBackedUp) {
4827                        mFullBackupQueue.add(which + 1, newEntry);
4828                        break;
4829                    }
4830                }
4831            }
4832            if (which < 0) {
4833                // this one is earlier than any existing one, so prepend
4834                mFullBackupQueue.add(0, newEntry);
4835            }
4836        }
4837        writeFullBackupScheduleAsync();
4838    }
4839
4840    private boolean fullBackupAllowable(IBackupTransport transport) {
4841        if (transport == null) {
4842            Slog.w(TAG, "Transport not present; full data backup not performed");
4843            return false;
4844        }
4845
4846        // Don't proceed unless we have already established package metadata
4847        // for the current dataset via a key/value backup pass.
4848        try {
4849            File stateDir = new File(mBaseStateDir, transport.transportDirName());
4850            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
4851            if (pmState.length() <= 0) {
4852                if (DEBUG) {
4853                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
4854                }
4855                return false;
4856            }
4857        } catch (Exception e) {
4858            Slog.w(TAG, "Unable to contact transport");
4859            return false;
4860        }
4861
4862        return true;
4863    }
4864
4865    /**
4866     * Conditions are right for a full backup operation, so run one.  The model we use is
4867     * to perform one app backup per scheduled job execution, and to reschedule the job
4868     * with zero latency as long as conditions remain right and we still have work to do.
4869     *
4870     * <p>This is the "start a full backup operation" entry point called by the scheduled job.
4871     *
4872     * @return Whether ongoing work will continue.  The return value here will be passed
4873     *         along as the return value to the scheduled job's onStartJob() callback.
4874     */
4875    boolean beginFullBackup(FullBackupJob scheduledJob) {
4876        long now = System.currentTimeMillis();
4877        FullBackupEntry entry = null;
4878        long latency = MIN_FULL_BACKUP_INTERVAL;
4879
4880        if (!mEnabled || !mProvisioned) {
4881            // Backups are globally disabled, so don't proceed.  We also don't reschedule
4882            // the job driving automatic backups; that job will be scheduled again when
4883            // the user enables backup.
4884            if (MORE_DEBUG) {
4885                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
4886                        + " p=" + mProvisioned + "; ignoring");
4887            }
4888            return false;
4889        }
4890
4891        // Don't run the backup if we're in battery saver mode, but reschedule
4892        // to try again in the not-so-distant future.
4893        if (mPowerManager.isPowerSaveMode()) {
4894            if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
4895            FullBackupJob.schedule(mContext, KeyValueBackupJob.BATCH_INTERVAL);
4896            return false;
4897        }
4898
4899        if (DEBUG_SCHEDULING) {
4900            Slog.i(TAG, "Beginning scheduled full backup operation");
4901        }
4902
4903        // Great; we're able to run full backup jobs now.  See if we have any work to do.
4904        synchronized (mQueueLock) {
4905            if (mRunningFullBackupTask != null) {
4906                Slog.e(TAG, "Backup triggered but one already/still running!");
4907                return false;
4908            }
4909
4910            // At this point we think that we have work to do, but possibly not right now.
4911            // Any exit without actually running backups will also require that we
4912            // reschedule the job.
4913            boolean runBackup = true;
4914            boolean headBusy;
4915
4916            do {
4917                // Recheck each time, because culling due to ineligibility may
4918                // have emptied the queue.
4919                if (mFullBackupQueue.size() == 0) {
4920                    // no work to do so just bow out
4921                    if (DEBUG) {
4922                        Slog.i(TAG, "Backup queue empty; doing nothing");
4923                    }
4924                    runBackup = false;
4925                    break;
4926                }
4927
4928                headBusy = false;
4929
4930                if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
4931                    if (MORE_DEBUG) {
4932                        Slog.i(TAG, "Preconditions not met; not running full backup");
4933                    }
4934                    runBackup = false;
4935                    // Typically this means we haven't run a key/value backup yet.  Back off
4936                    // full-backup operations by the key/value job's run interval so that
4937                    // next time we run, we are likely to be able to make progress.
4938                    latency = KeyValueBackupJob.BATCH_INTERVAL;
4939                }
4940
4941                if (runBackup) {
4942                    entry = mFullBackupQueue.get(0);
4943                    long timeSinceRun = now - entry.lastBackup;
4944                    runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
4945                    if (!runBackup) {
4946                        // It's too early to back up the next thing in the queue, so bow out
4947                        if (MORE_DEBUG) {
4948                            Slog.i(TAG, "Device ready but too early to back up next app");
4949                        }
4950                        // Wait until the next app in the queue falls due for a full data backup
4951                        latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
4952                        break;  // we know we aren't doing work yet, so bail.
4953                    }
4954
4955                    try {
4956                        PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
4957                        if (!appGetsFullBackup(appInfo)) {
4958                            // The head app isn't supposed to get full-data backups [any more];
4959                            // so we cull it and force a loop around to consider the new head
4960                            // app.
4961                            if (MORE_DEBUG) {
4962                                Slog.i(TAG, "Culling package " + entry.packageName
4963                                        + " in full-backup queue but not eligible");
4964                            }
4965                            mFullBackupQueue.remove(0);
4966                            headBusy = true; // force the while() condition
4967                            continue;
4968                        }
4969
4970                        final int privFlags = appInfo.applicationInfo.privateFlags;
4971                        headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
4972                                && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
4973
4974                        if (headBusy) {
4975                            final long nextEligible = System.currentTimeMillis()
4976                                    + BUSY_BACKOFF_MIN_MILLIS
4977                                    + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
4978                            if (DEBUG_SCHEDULING) {
4979                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4980                                Slog.i(TAG, "Full backup time but " + entry.packageName
4981                                        + " is busy; deferring to "
4982                                        + sdf.format(new Date(nextEligible)));
4983                            }
4984                            // This relocates the app's entry from the head of the queue to
4985                            // its order-appropriate position further down, so upon looping
4986                            // a new candidate will be considered at the head.
4987                            enqueueFullBackup(entry.packageName,
4988                                    nextEligible - MIN_FULL_BACKUP_INTERVAL);
4989                        }
4990                    } catch (NameNotFoundException nnf) {
4991                        // So, we think we want to back this up, but it turns out the package
4992                        // in question is no longer installed.  We want to drop it from the
4993                        // queue entirely and move on, but if there's nothing else in the queue
4994                        // we should bail entirely.  headBusy cannot have been set to true yet.
4995                        runBackup = (mFullBackupQueue.size() > 1);
4996                    } catch (RemoteException e) {
4997                        // Cannot happen; the Activity Manager is in the same process
4998                    }
4999                }
5000            } while (headBusy);
5001
5002            if (!runBackup) {
5003                if (DEBUG_SCHEDULING) {
5004                    Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
5005                }
5006                final long deferTime = latency;     // pin for the closure
5007                mBackupHandler.post(new Runnable() {
5008                    @Override public void run() {
5009                        FullBackupJob.schedule(mContext, deferTime);
5010                    }
5011                });
5012                return false;
5013            }
5014
5015            // Okay, the top thing is ready for backup now.  Do it.
5016            mFullBackupQueue.remove(0);
5017            CountDownLatch latch = new CountDownLatch(1);
5018            String[] pkg = new String[] {entry.packageName};
5019            mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
5020                    scheduledJob, latch, null, false /* userInitiated */);
5021            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
5022            mWakelock.acquire();
5023            (new Thread(mRunningFullBackupTask)).start();
5024        }
5025
5026        return true;
5027    }
5028
5029    // The job scheduler says our constraints don't hold any more,
5030    // so tear down any ongoing backup task right away.
5031    void endFullBackup() {
5032        synchronized (mQueueLock) {
5033            if (mRunningFullBackupTask != null) {
5034                if (DEBUG_SCHEDULING) {
5035                    Slog.i(TAG, "Telling running backup to stop");
5036                }
5037                mRunningFullBackupTask.setRunning(false);
5038            }
5039        }
5040    }
5041
5042    // ----- Restore infrastructure -----
5043
5044    abstract class RestoreEngine {
5045        static final String TAG = "RestoreEngine";
5046
5047        public static final int SUCCESS = 0;
5048        public static final int TARGET_FAILURE = -2;
5049        public static final int TRANSPORT_FAILURE = -3;
5050
5051        private AtomicBoolean mRunning = new AtomicBoolean(false);
5052        private AtomicInteger mResult = new AtomicInteger(SUCCESS);
5053
5054        public boolean isRunning() {
5055            return mRunning.get();
5056        }
5057
5058        public void setRunning(boolean stillRunning) {
5059            synchronized (mRunning) {
5060                mRunning.set(stillRunning);
5061                mRunning.notifyAll();
5062            }
5063        }
5064
5065        public int waitForResult() {
5066            synchronized (mRunning) {
5067                while (isRunning()) {
5068                    try {
5069                        mRunning.wait();
5070                    } catch (InterruptedException e) {}
5071                }
5072            }
5073            return getResult();
5074        }
5075
5076        public int getResult() {
5077            return mResult.get();
5078        }
5079
5080        public void setResult(int result) {
5081            mResult.set(result);
5082        }
5083
5084        // TODO: abstract restore state and APIs
5085    }
5086
5087    // ----- Full restore from a file/socket -----
5088
5089    // Description of a file in the restore datastream
5090    static class FileMetadata {
5091        String packageName;             // name of the owning app
5092        String installerPackageName;    // name of the market-type app that installed the owner
5093        int type;                       // e.g. BackupAgent.TYPE_DIRECTORY
5094        String domain;                  // e.g. FullBackup.DATABASE_TREE_TOKEN
5095        String path;                    // subpath within the semantic domain
5096        long mode;                      // e.g. 0666 (actually int)
5097        long mtime;                     // last mod time, UTC time_t (actually int)
5098        long size;                      // bytes of content
5099
5100        @Override
5101        public String toString() {
5102            StringBuilder sb = new StringBuilder(128);
5103            sb.append("FileMetadata{");
5104            sb.append(packageName); sb.append(',');
5105            sb.append(type); sb.append(',');
5106            sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
5107            sb.append(size);
5108            sb.append('}');
5109            return sb.toString();
5110        }
5111    }
5112
5113    enum RestorePolicy {
5114        IGNORE,
5115        ACCEPT,
5116        ACCEPT_IF_APK
5117    }
5118
5119    // Full restore engine, used by both adb restore and transport-based full restore
5120    class FullRestoreEngine extends RestoreEngine {
5121        // Dedicated observer, if any
5122        IFullBackupRestoreObserver mObserver;
5123
5124        // Where we're delivering the file data as we go
5125        IBackupAgent mAgent;
5126
5127        // Are we permitted to only deliver a specific package's metadata?
5128        PackageInfo mOnlyPackage;
5129
5130        boolean mAllowApks;
5131        boolean mAllowObbs;
5132
5133        // Which package are we currently handling data for?
5134        String mAgentPackage;
5135
5136        // Info for working with the target app process
5137        ApplicationInfo mTargetApp;
5138
5139        // Machinery for restoring OBBs
5140        FullBackupObbConnection mObbConnection = null;
5141
5142        // possible handling states for a given package in the restore dataset
5143        final HashMap<String, RestorePolicy> mPackagePolicies
5144                = new HashMap<String, RestorePolicy>();
5145
5146        // installer package names for each encountered app, derived from the manifests
5147        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
5148
5149        // Signatures for a given package found in its manifest file
5150        final HashMap<String, Signature[]> mManifestSignatures
5151                = new HashMap<String, Signature[]>();
5152
5153        // Packages we've already wiped data on when restoring their first file
5154        final HashSet<String> mClearedPackages = new HashSet<String>();
5155
5156        // How much data have we moved?
5157        long mBytes;
5158
5159        // Working buffer
5160        byte[] mBuffer;
5161
5162        // Pipes for moving data
5163        ParcelFileDescriptor[] mPipes = null;
5164
5165        // Widget blob to be restored out-of-band
5166        byte[] mWidgetData = null;
5167
5168        // Runner that can be placed in a separate thread to do in-process
5169        // invocations of the full restore API asynchronously
5170        class RestoreFileRunnable implements Runnable {
5171            IBackupAgent mAgent;
5172            FileMetadata mInfo;
5173            ParcelFileDescriptor mSocket;
5174            int mToken;
5175
5176            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
5177                    ParcelFileDescriptor socket, int token) throws IOException {
5178                mAgent = agent;
5179                mInfo = info;
5180                mToken = token;
5181
5182                // This class is used strictly for process-local binder invocations.  The
5183                // semantics of ParcelFileDescriptor differ in this case; in particular, we
5184                // do not automatically get a 'dup'ed descriptor that we can can continue
5185                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
5186                // before proceeding to do the restore.
5187                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
5188            }
5189
5190            @Override
5191            public void run() {
5192                try {
5193                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
5194                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
5195                            mToken, mBackupManagerBinder);
5196                } catch (RemoteException e) {
5197                    // never happens; this is used strictly for local binder calls
5198                }
5199            }
5200        }
5201
5202        public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage,
5203                boolean allowApks, boolean allowObbs) {
5204            mObserver = observer;
5205            mOnlyPackage = onlyPackage;
5206            mAllowApks = allowApks;
5207            mAllowObbs = allowObbs;
5208            mBuffer = new byte[32 * 1024];
5209            mBytes = 0;
5210        }
5211
5212        public IBackupAgent getAgent() {
5213            return mAgent;
5214        }
5215
5216        public boolean restoreOneFile(InputStream instream, boolean mustKillAgent) {
5217            if (!isRunning()) {
5218                Slog.w(TAG, "Restore engine used after halting");
5219                return false;
5220            }
5221
5222            FileMetadata info;
5223            try {
5224                if (MORE_DEBUG) {
5225                    Slog.v(TAG, "Reading tar header for restoring file");
5226                }
5227                info = readTarHeaders(instream);
5228                if (info != null) {
5229                    if (MORE_DEBUG) {
5230                        dumpFileMetadata(info);
5231                    }
5232
5233                    final String pkg = info.packageName;
5234                    if (!pkg.equals(mAgentPackage)) {
5235                        // In the single-package case, it's a semantic error to expect
5236                        // one app's data but see a different app's on the wire
5237                        if (mOnlyPackage != null) {
5238                            if (!pkg.equals(mOnlyPackage.packageName)) {
5239                                Slog.w(TAG, "Expected data for " + mOnlyPackage
5240                                        + " but saw " + pkg);
5241                                setResult(RestoreEngine.TRANSPORT_FAILURE);
5242                                setRunning(false);
5243                                return false;
5244                            }
5245                        }
5246
5247                        // okay, change in package; set up our various
5248                        // bookkeeping if we haven't seen it yet
5249                        if (!mPackagePolicies.containsKey(pkg)) {
5250                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5251                        }
5252
5253                        // Clean up the previous agent relationship if necessary,
5254                        // and let the observer know we're considering a new app.
5255                        if (mAgent != null) {
5256                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
5257                            // Now we're really done
5258                            tearDownPipes();
5259                            tearDownAgent(mTargetApp);
5260                            mTargetApp = null;
5261                            mAgentPackage = null;
5262                        }
5263                    }
5264
5265                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
5266                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
5267                        mPackageInstallers.put(pkg, info.installerPackageName);
5268                        // We've read only the manifest content itself at this point,
5269                        // so consume the footer before looping around to the next
5270                        // input file
5271                        skipTarPadding(info.size, instream);
5272                        sendOnRestorePackage(pkg);
5273                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
5274                        // Metadata blobs!
5275                        readMetadata(info, instream);
5276                        skipTarPadding(info.size, instream);
5277                    } else {
5278                        // Non-manifest, so it's actual file data.  Is this a package
5279                        // we're ignoring?
5280                        boolean okay = true;
5281                        RestorePolicy policy = mPackagePolicies.get(pkg);
5282                        switch (policy) {
5283                            case IGNORE:
5284                                okay = false;
5285                                break;
5286
5287                            case ACCEPT_IF_APK:
5288                                // If we're in accept-if-apk state, then the first file we
5289                                // see MUST be the apk.
5290                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5291                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
5292                                    // Try to install the app.
5293                                    String installerName = mPackageInstallers.get(pkg);
5294                                    okay = installApk(info, installerName, instream);
5295                                    // good to go; promote to ACCEPT
5296                                    mPackagePolicies.put(pkg, (okay)
5297                                            ? RestorePolicy.ACCEPT
5298                                                    : RestorePolicy.IGNORE);
5299                                    // At this point we've consumed this file entry
5300                                    // ourselves, so just strip the tar footer and
5301                                    // go on to the next file in the input stream
5302                                    skipTarPadding(info.size, instream);
5303                                    return true;
5304                                } else {
5305                                    // File data before (or without) the apk.  We can't
5306                                    // handle it coherently in this case so ignore it.
5307                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5308                                    okay = false;
5309                                }
5310                                break;
5311
5312                            case ACCEPT:
5313                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5314                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
5315                                    // we can take the data without the apk, so we
5316                                    // *want* to do so.  skip the apk by declaring this
5317                                    // one file not-okay without changing the restore
5318                                    // policy for the package.
5319                                    okay = false;
5320                                }
5321                                break;
5322
5323                            default:
5324                                // Something has gone dreadfully wrong when determining
5325                                // the restore policy from the manifest.  Ignore the
5326                                // rest of this package's data.
5327                                Slog.e(TAG, "Invalid policy from manifest");
5328                                okay = false;
5329                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5330                                break;
5331                        }
5332
5333                        // Is it a *file* we need to drop?
5334                        if (!isRestorableFile(info)) {
5335                            okay = false;
5336                        }
5337
5338                        // If the policy is satisfied, go ahead and set up to pipe the
5339                        // data to the agent.
5340                        if (DEBUG && okay && mAgent != null) {
5341                            Slog.i(TAG, "Reusing existing agent instance");
5342                        }
5343                        if (okay && mAgent == null) {
5344                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
5345
5346                            try {
5347                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
5348
5349                                // If we haven't sent any data to this app yet, we probably
5350                                // need to clear it first.  Check that.
5351                                if (!mClearedPackages.contains(pkg)) {
5352                                    // apps with their own backup agents are
5353                                    // responsible for coherently managing a full
5354                                    // restore.
5355                                    if (mTargetApp.backupAgentName == null) {
5356                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
5357                                        clearApplicationDataSynchronous(pkg);
5358                                    } else {
5359                                        if (MORE_DEBUG) Slog.d(TAG, "backup agent ("
5360                                                + mTargetApp.backupAgentName + ") => no clear");
5361                                    }
5362                                    mClearedPackages.add(pkg);
5363                                } else {
5364                                    if (MORE_DEBUG) {
5365                                        Slog.d(TAG, "We've initialized this app already; no clear required");
5366                                    }
5367                                }
5368
5369                                // All set; now set up the IPC and launch the agent
5370                                setUpPipes();
5371                                mAgent = bindToAgentSynchronous(mTargetApp,
5372                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
5373                                mAgentPackage = pkg;
5374                            } catch (IOException e) {
5375                                // fall through to error handling
5376                            } catch (NameNotFoundException e) {
5377                                // fall through to error handling
5378                            }
5379
5380                            if (mAgent == null) {
5381                                Slog.e(TAG, "Unable to create agent for " + pkg);
5382                                okay = false;
5383                                tearDownPipes();
5384                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5385                            }
5386                        }
5387
5388                        // Sanity check: make sure we never give data to the wrong app.  This
5389                        // should never happen but a little paranoia here won't go amiss.
5390                        if (okay && !pkg.equals(mAgentPackage)) {
5391                            Slog.e(TAG, "Restoring data for " + pkg
5392                                    + " but agent is for " + mAgentPackage);
5393                            okay = false;
5394                        }
5395
5396                        // At this point we have an agent ready to handle the full
5397                        // restore data as well as a pipe for sending data to
5398                        // that agent.  Tell the agent to start reading from the
5399                        // pipe.
5400                        if (okay) {
5401                            boolean agentSuccess = true;
5402                            long toCopy = info.size;
5403                            final int token = generateToken();
5404                            try {
5405                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
5406                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
5407                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
5408                                            + " : " + info.path);
5409                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
5410                                            info.size, info.type, info.path, info.mode,
5411                                            info.mtime, token, mBackupManagerBinder);
5412                                } else {
5413                                    if (MORE_DEBUG) Slog.d(TAG, "Invoking agent to restore file "
5414                                            + info.path);
5415                                    // fire up the app's agent listening on the socket.  If
5416                                    // the agent is running in the system process we can't
5417                                    // just invoke it asynchronously, so we provide a thread
5418                                    // for it here.
5419                                    if (mTargetApp.processName.equals("system")) {
5420                                        Slog.d(TAG, "system process agent - spinning a thread");
5421                                        RestoreFileRunnable runner = new RestoreFileRunnable(
5422                                                mAgent, info, mPipes[0], token);
5423                                        new Thread(runner, "restore-sys-runner").start();
5424                                    } else {
5425                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
5426                                                info.domain, info.path, info.mode, info.mtime,
5427                                                token, mBackupManagerBinder);
5428                                    }
5429                                }
5430                            } catch (IOException e) {
5431                                // couldn't dup the socket for a process-local restore
5432                                Slog.d(TAG, "Couldn't establish restore");
5433                                agentSuccess = false;
5434                                okay = false;
5435                            } catch (RemoteException e) {
5436                                // whoops, remote entity went away.  We'll eat the content
5437                                // ourselves, then, and not copy it over.
5438                                Slog.e(TAG, "Agent crashed during full restore");
5439                                agentSuccess = false;
5440                                okay = false;
5441                            }
5442
5443                            // Copy over the data if the agent is still good
5444                            if (okay) {
5445                                if (MORE_DEBUG) {
5446                                    Slog.v(TAG, "  copying to restore agent: "
5447                                            + toCopy + " bytes");
5448                                }
5449                                boolean pipeOkay = true;
5450                                FileOutputStream pipe = new FileOutputStream(
5451                                        mPipes[1].getFileDescriptor());
5452                                while (toCopy > 0) {
5453                                    int toRead = (toCopy > mBuffer.length)
5454                                            ? mBuffer.length : (int)toCopy;
5455                                    int nRead = instream.read(mBuffer, 0, toRead);
5456                                    if (nRead >= 0) mBytes += nRead;
5457                                    if (nRead <= 0) break;
5458                                    toCopy -= nRead;
5459
5460                                    // send it to the output pipe as long as things
5461                                    // are still good
5462                                    if (pipeOkay) {
5463                                        try {
5464                                            pipe.write(mBuffer, 0, nRead);
5465                                        } catch (IOException e) {
5466                                            Slog.e(TAG, "Failed to write to restore pipe", e);
5467                                            pipeOkay = false;
5468                                        }
5469                                    }
5470                                }
5471
5472                                // done sending that file!  Now we just need to consume
5473                                // the delta from info.size to the end of block.
5474                                skipTarPadding(info.size, instream);
5475
5476                                // and now that we've sent it all, wait for the remote
5477                                // side to acknowledge receipt
5478                                agentSuccess = waitUntilOperationComplete(token);
5479                            }
5480
5481                            // okay, if the remote end failed at any point, deal with
5482                            // it by ignoring the rest of the restore on it
5483                            if (!agentSuccess) {
5484                                Slog.w(TAG, "Agent failure; ending restore");
5485                                mBackupHandler.removeMessages(MSG_TIMEOUT);
5486                                tearDownPipes();
5487                                tearDownAgent(mTargetApp);
5488                                mAgent = null;
5489                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5490
5491                                // If this was a single-package restore, we halt immediately
5492                                // with an agent error under these circumstances
5493                                if (mOnlyPackage != null) {
5494                                    setResult(RestoreEngine.TARGET_FAILURE);
5495                                    setRunning(false);
5496                                    return false;
5497                                }
5498                            }
5499                        }
5500
5501                        // Problems setting up the agent communication, an explicitly
5502                        // dropped file, or an already-ignored package: skip to the
5503                        // next stream entry by reading and discarding this file.
5504                        if (!okay) {
5505                            if (MORE_DEBUG) Slog.d(TAG, "[discarding file content]");
5506                            long bytesToConsume = (info.size + 511) & ~511;
5507                            while (bytesToConsume > 0) {
5508                                int toRead = (bytesToConsume > mBuffer.length)
5509                                        ? mBuffer.length : (int)bytesToConsume;
5510                                long nRead = instream.read(mBuffer, 0, toRead);
5511                                if (nRead >= 0) mBytes += nRead;
5512                                if (nRead <= 0) break;
5513                                bytesToConsume -= nRead;
5514                            }
5515                        }
5516                    }
5517                }
5518            } catch (IOException e) {
5519                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
5520                setResult(RestoreEngine.TRANSPORT_FAILURE);
5521                info = null;
5522            }
5523
5524            // If we got here we're either running smoothly or we've finished
5525            if (info == null) {
5526                if (MORE_DEBUG) {
5527                    Slog.i(TAG, "No [more] data for this package; tearing down");
5528                }
5529                tearDownPipes();
5530                setRunning(false);
5531                if (mustKillAgent) {
5532                    tearDownAgent(mTargetApp);
5533                }
5534            }
5535            return (info != null);
5536        }
5537
5538        void setUpPipes() throws IOException {
5539            mPipes = ParcelFileDescriptor.createPipe();
5540        }
5541
5542        void tearDownPipes() {
5543            if (mPipes != null) {
5544                try {
5545                    mPipes[0].close();
5546                    mPipes[0] = null;
5547                    mPipes[1].close();
5548                    mPipes[1] = null;
5549                } catch (IOException e) {
5550                    Slog.w(TAG, "Couldn't close agent pipes", e);
5551                }
5552                mPipes = null;
5553            }
5554        }
5555
5556        void tearDownAgent(ApplicationInfo app) {
5557            if (mAgent != null) {
5558                tearDownAgentAndKill(app);
5559                mAgent = null;
5560            }
5561        }
5562
5563        class RestoreInstallObserver extends PackageInstallObserver {
5564            final AtomicBoolean mDone = new AtomicBoolean();
5565            String mPackageName;
5566            int mResult;
5567
5568            public void reset() {
5569                synchronized (mDone) {
5570                    mDone.set(false);
5571                }
5572            }
5573
5574            public void waitForCompletion() {
5575                synchronized (mDone) {
5576                    while (mDone.get() == false) {
5577                        try {
5578                            mDone.wait();
5579                        } catch (InterruptedException e) { }
5580                    }
5581                }
5582            }
5583
5584            int getResult() {
5585                return mResult;
5586            }
5587
5588            @Override
5589            public void onPackageInstalled(String packageName, int returnCode,
5590                    String msg, Bundle extras) {
5591                synchronized (mDone) {
5592                    mResult = returnCode;
5593                    mPackageName = packageName;
5594                    mDone.set(true);
5595                    mDone.notifyAll();
5596                }
5597            }
5598        }
5599
5600        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
5601            final AtomicBoolean mDone = new AtomicBoolean();
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            @Override
5621            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
5622                synchronized (mDone) {
5623                    mResult = returnCode;
5624                    mDone.set(true);
5625                    mDone.notifyAll();
5626                }
5627            }
5628        }
5629
5630        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
5631        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
5632
5633        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
5634            boolean okay = true;
5635
5636            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
5637
5638            // The file content is an .apk file.  Copy it out to a staging location and
5639            // attempt to install it.
5640            File apkFile = new File(mDataDir, info.packageName);
5641            try {
5642                FileOutputStream apkStream = new FileOutputStream(apkFile);
5643                byte[] buffer = new byte[32 * 1024];
5644                long size = info.size;
5645                while (size > 0) {
5646                    long toRead = (buffer.length < size) ? buffer.length : size;
5647                    int didRead = instream.read(buffer, 0, (int)toRead);
5648                    if (didRead >= 0) mBytes += didRead;
5649                    apkStream.write(buffer, 0, didRead);
5650                    size -= didRead;
5651                }
5652                apkStream.close();
5653
5654                // make sure the installer can read it
5655                apkFile.setReadable(true, false);
5656
5657                // Now install it
5658                Uri packageUri = Uri.fromFile(apkFile);
5659                mInstallObserver.reset();
5660                mPackageManager.installPackage(packageUri, mInstallObserver,
5661                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
5662                        installerPackage);
5663                mInstallObserver.waitForCompletion();
5664
5665                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
5666                    // The only time we continue to accept install of data even if the
5667                    // apk install failed is if we had already determined that we could
5668                    // accept the data regardless.
5669                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
5670                        okay = false;
5671                    }
5672                } else {
5673                    // Okay, the install succeeded.  Make sure it was the right app.
5674                    boolean uninstall = false;
5675                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
5676                        Slog.w(TAG, "Restore stream claimed to include apk for "
5677                                + info.packageName + " but apk was really "
5678                                + mInstallObserver.mPackageName);
5679                        // delete the package we just put in place; it might be fraudulent
5680                        okay = false;
5681                        uninstall = true;
5682                    } else {
5683                        try {
5684                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
5685                                    PackageManager.GET_SIGNATURES);
5686                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
5687                                Slog.w(TAG, "Restore stream contains apk of package "
5688                                        + info.packageName + " but it disallows backup/restore");
5689                                okay = false;
5690                            } else {
5691                                // So far so good -- do the signatures match the manifest?
5692                                Signature[] sigs = mManifestSignatures.get(info.packageName);
5693                                if (signaturesMatch(sigs, pkg)) {
5694                                    // If this is a system-uid app without a declared backup agent,
5695                                    // don't restore any of the file data.
5696                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
5697                                            && (pkg.applicationInfo.backupAgentName == null)) {
5698                                        Slog.w(TAG, "Installed app " + info.packageName
5699                                                + " has restricted uid and no agent");
5700                                        okay = false;
5701                                    }
5702                                } else {
5703                                    Slog.w(TAG, "Installed app " + info.packageName
5704                                            + " signatures do not match restore manifest");
5705                                    okay = false;
5706                                    uninstall = true;
5707                                }
5708                            }
5709                        } catch (NameNotFoundException e) {
5710                            Slog.w(TAG, "Install of package " + info.packageName
5711                                    + " succeeded but now not found");
5712                            okay = false;
5713                        }
5714                    }
5715
5716                    // If we're not okay at this point, we need to delete the package
5717                    // that we just installed.
5718                    if (uninstall) {
5719                        mDeleteObserver.reset();
5720                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
5721                                mDeleteObserver, 0);
5722                        mDeleteObserver.waitForCompletion();
5723                    }
5724                }
5725            } catch (IOException e) {
5726                Slog.e(TAG, "Unable to transcribe restored apk for install");
5727                okay = false;
5728            } finally {
5729                apkFile.delete();
5730            }
5731
5732            return okay;
5733        }
5734
5735        // Given an actual file content size, consume the post-content padding mandated
5736        // by the tar format.
5737        void skipTarPadding(long size, InputStream instream) throws IOException {
5738            long partial = (size + 512) % 512;
5739            if (partial > 0) {
5740                final int needed = 512 - (int)partial;
5741                if (MORE_DEBUG) {
5742                    Slog.i(TAG, "Skipping tar padding: " + needed + " bytes");
5743                }
5744                byte[] buffer = new byte[needed];
5745                if (readExactly(instream, buffer, 0, needed) == needed) {
5746                    mBytes += needed;
5747                } else throw new IOException("Unexpected EOF in padding");
5748            }
5749        }
5750
5751        // Read a widget metadata file, returning the restored blob
5752        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
5753            // Fail on suspiciously large widget dump files
5754            if (info.size > 64 * 1024) {
5755                throw new IOException("Metadata too big; corrupt? size=" + info.size);
5756            }
5757
5758            byte[] buffer = new byte[(int) info.size];
5759            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5760                mBytes += info.size;
5761            } else throw new IOException("Unexpected EOF in widget data");
5762
5763            String[] str = new String[1];
5764            int offset = extractLine(buffer, 0, str);
5765            int version = Integer.parseInt(str[0]);
5766            if (version == BACKUP_MANIFEST_VERSION) {
5767                offset = extractLine(buffer, offset, str);
5768                final String pkg = str[0];
5769                if (info.packageName.equals(pkg)) {
5770                    // Data checks out -- the rest of the buffer is a concatenation of
5771                    // binary blobs as described in the comment at writeAppWidgetData()
5772                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
5773                            offset, buffer.length - offset);
5774                    DataInputStream in = new DataInputStream(bin);
5775                    while (bin.available() > 0) {
5776                        int token = in.readInt();
5777                        int size = in.readInt();
5778                        if (size > 64 * 1024) {
5779                            throw new IOException("Datum "
5780                                    + Integer.toHexString(token)
5781                                    + " too big; corrupt? size=" + info.size);
5782                        }
5783                        switch (token) {
5784                            case BACKUP_WIDGET_METADATA_TOKEN:
5785                            {
5786                                if (MORE_DEBUG) {
5787                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
5788                                }
5789                                mWidgetData = new byte[size];
5790                                in.read(mWidgetData);
5791                                break;
5792                            }
5793                            default:
5794                            {
5795                                if (DEBUG) {
5796                                    Slog.i(TAG, "Ignoring metadata blob "
5797                                            + Integer.toHexString(token)
5798                                            + " for " + info.packageName);
5799                                }
5800                                in.skipBytes(size);
5801                                break;
5802                            }
5803                        }
5804                    }
5805                } else {
5806                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
5807                            + " but widget data for " + pkg);
5808                }
5809            } else {
5810                Slog.w(TAG, "Unsupported metadata version " + version);
5811            }
5812        }
5813
5814        // Returns a policy constant
5815        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
5816                throws IOException {
5817            // Fail on suspiciously large manifest files
5818            if (info.size > 64 * 1024) {
5819                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
5820            }
5821
5822            byte[] buffer = new byte[(int) info.size];
5823            if (MORE_DEBUG) {
5824                Slog.i(TAG, "   readAppManifest() looking for " + info.size + " bytes, "
5825                        + mBytes + " already consumed");
5826            }
5827            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5828                mBytes += info.size;
5829            } else throw new IOException("Unexpected EOF in manifest");
5830
5831            RestorePolicy policy = RestorePolicy.IGNORE;
5832            String[] str = new String[1];
5833            int offset = 0;
5834
5835            try {
5836                offset = extractLine(buffer, offset, str);
5837                int version = Integer.parseInt(str[0]);
5838                if (version == BACKUP_MANIFEST_VERSION) {
5839                    offset = extractLine(buffer, offset, str);
5840                    String manifestPackage = str[0];
5841                    // TODO: handle <original-package>
5842                    if (manifestPackage.equals(info.packageName)) {
5843                        offset = extractLine(buffer, offset, str);
5844                        version = Integer.parseInt(str[0]);  // app version
5845                        offset = extractLine(buffer, offset, str);
5846                        // This is the platform version, which we don't use, but we parse it
5847                        // as a safety against corruption in the manifest.
5848                        Integer.parseInt(str[0]);
5849                        offset = extractLine(buffer, offset, str);
5850                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
5851                        offset = extractLine(buffer, offset, str);
5852                        boolean hasApk = str[0].equals("1");
5853                        offset = extractLine(buffer, offset, str);
5854                        int numSigs = Integer.parseInt(str[0]);
5855                        if (numSigs > 0) {
5856                            Signature[] sigs = new Signature[numSigs];
5857                            for (int i = 0; i < numSigs; i++) {
5858                                offset = extractLine(buffer, offset, str);
5859                                sigs[i] = new Signature(str[0]);
5860                            }
5861                            mManifestSignatures.put(info.packageName, sigs);
5862
5863                            // Okay, got the manifest info we need...
5864                            try {
5865                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
5866                                        info.packageName, PackageManager.GET_SIGNATURES);
5867                                // Fall through to IGNORE if the app explicitly disallows backup
5868                                final int flags = pkgInfo.applicationInfo.flags;
5869                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
5870                                    // Restore system-uid-space packages only if they have
5871                                    // defined a custom backup agent
5872                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
5873                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
5874                                        // Verify signatures against any installed version; if they
5875                                        // don't match, then we fall though and ignore the data.  The
5876                                        // signatureMatch() method explicitly ignores the signature
5877                                        // check for packages installed on the system partition, because
5878                                        // such packages are signed with the platform cert instead of
5879                                        // the app developer's cert, so they're different on every
5880                                        // device.
5881                                        if (signaturesMatch(sigs, pkgInfo)) {
5882                                            if (pkgInfo.versionCode >= version) {
5883                                                Slog.i(TAG, "Sig + version match; taking data");
5884                                                policy = RestorePolicy.ACCEPT;
5885                                            } else {
5886                                                // The data is from a newer version of the app than
5887                                                // is presently installed.  That means we can only
5888                                                // use it if the matching apk is also supplied.
5889                                                if (mAllowApks) {
5890                                                    Slog.i(TAG, "Data version " + version
5891                                                            + " is newer than installed version "
5892                                                            + pkgInfo.versionCode
5893                                                            + " - requiring apk");
5894                                                    policy = RestorePolicy.ACCEPT_IF_APK;
5895                                                } else {
5896                                                    Slog.i(TAG, "Data requires newer version "
5897                                                            + version + "; ignoring");
5898                                                    policy = RestorePolicy.IGNORE;
5899                                                }
5900                                            }
5901                                        } else {
5902                                            Slog.w(TAG, "Restore manifest signatures do not match "
5903                                                    + "installed application for " + info.packageName);
5904                                        }
5905                                    } else {
5906                                        Slog.w(TAG, "Package " + info.packageName
5907                                                + " is system level with no agent");
5908                                    }
5909                                } else {
5910                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
5911                                            + info.packageName + " but allowBackup=false");
5912                                }
5913                            } catch (NameNotFoundException e) {
5914                                // Okay, the target app isn't installed.  We can process
5915                                // the restore properly only if the dataset provides the
5916                                // apk file and we can successfully install it.
5917                                if (mAllowApks) {
5918                                    if (DEBUG) Slog.i(TAG, "Package " + info.packageName
5919                                            + " not installed; requiring apk in dataset");
5920                                    policy = RestorePolicy.ACCEPT_IF_APK;
5921                                } else {
5922                                    policy = RestorePolicy.IGNORE;
5923                                }
5924                            }
5925
5926                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
5927                                Slog.i(TAG, "Cannot restore package " + info.packageName
5928                                        + " without the matching .apk");
5929                            }
5930                        } else {
5931                            Slog.i(TAG, "Missing signature on backed-up package "
5932                                    + info.packageName);
5933                        }
5934                    } else {
5935                        Slog.i(TAG, "Expected package " + info.packageName
5936                                + " but restore manifest claims " + manifestPackage);
5937                    }
5938                } else {
5939                    Slog.i(TAG, "Unknown restore manifest version " + version
5940                            + " for package " + info.packageName);
5941                }
5942            } catch (NumberFormatException e) {
5943                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
5944            } catch (IllegalArgumentException e) {
5945                Slog.w(TAG, e.getMessage());
5946            }
5947
5948            return policy;
5949        }
5950
5951        // Builds a line from a byte buffer starting at 'offset', and returns
5952        // the index of the next unconsumed data in the buffer.
5953        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
5954            final int end = buffer.length;
5955            if (offset >= end) throw new IOException("Incomplete data");
5956
5957            int pos;
5958            for (pos = offset; pos < end; pos++) {
5959                byte c = buffer[pos];
5960                // at LF we declare end of line, and return the next char as the
5961                // starting point for the next time through
5962                if (c == '\n') {
5963                    break;
5964                }
5965            }
5966            outStr[0] = new String(buffer, offset, pos - offset);
5967            pos++;  // may be pointing an extra byte past the end but that's okay
5968            return pos;
5969        }
5970
5971        void dumpFileMetadata(FileMetadata info) {
5972            if (MORE_DEBUG) {
5973                StringBuilder b = new StringBuilder(128);
5974
5975                // mode string
5976                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
5977                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
5978                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
5979                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
5980                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
5981                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
5982                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
5983                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
5984                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
5985                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
5986                b.append(String.format(" %9d ", info.size));
5987
5988                Date stamp = new Date(info.mtime);
5989                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
5990
5991                b.append(info.packageName);
5992                b.append(" :: ");
5993                b.append(info.domain);
5994                b.append(" :: ");
5995                b.append(info.path);
5996
5997                Slog.i(TAG, b.toString());
5998            }
5999        }
6000
6001        // Consume a tar file header block [sequence] and accumulate the relevant metadata
6002        FileMetadata readTarHeaders(InputStream instream) throws IOException {
6003            byte[] block = new byte[512];
6004            FileMetadata info = null;
6005
6006            boolean gotHeader = readTarHeader(instream, block);
6007            if (gotHeader) {
6008                try {
6009                    // okay, presume we're okay, and extract the various metadata
6010                    info = new FileMetadata();
6011                    info.size = extractRadix(block, 124, 12, 8);
6012                    info.mtime = extractRadix(block, 136, 12, 8);
6013                    info.mode = extractRadix(block, 100, 8, 8);
6014
6015                    info.path = extractString(block, 345, 155); // prefix
6016                    String path = extractString(block, 0, 100);
6017                    if (path.length() > 0) {
6018                        if (info.path.length() > 0) info.path += '/';
6019                        info.path += path;
6020                    }
6021
6022                    // tar link indicator field: 1 byte at offset 156 in the header.
6023                    int typeChar = block[156];
6024                    if (typeChar == 'x') {
6025                        // pax extended header, so we need to read that
6026                        gotHeader = readPaxExtendedHeader(instream, info);
6027                        if (gotHeader) {
6028                            // and after a pax extended header comes another real header -- read
6029                            // that to find the real file type
6030                            gotHeader = readTarHeader(instream, block);
6031                        }
6032                        if (!gotHeader) throw new IOException("Bad or missing pax header");
6033
6034                        typeChar = block[156];
6035                    }
6036
6037                    switch (typeChar) {
6038                        case '0': info.type = BackupAgent.TYPE_FILE; break;
6039                        case '5': {
6040                            info.type = BackupAgent.TYPE_DIRECTORY;
6041                            if (info.size != 0) {
6042                                Slog.w(TAG, "Directory entry with nonzero size in header");
6043                                info.size = 0;
6044                            }
6045                            break;
6046                        }
6047                        case 0: {
6048                            // presume EOF
6049                            if (MORE_DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
6050                            return null;
6051                        }
6052                        default: {
6053                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
6054                            throw new IOException("Unknown entity type " + typeChar);
6055                        }
6056                    }
6057
6058                    // Parse out the path
6059                    //
6060                    // first: apps/shared/unrecognized
6061                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
6062                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
6063                        // File in shared storage.  !!! TODO: implement this.
6064                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
6065                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
6066                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
6067                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
6068                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
6069                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
6070                        // App content!  Parse out the package name and domain
6071
6072                        // strip the apps/ prefix
6073                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
6074
6075                        // extract the package name
6076                        int slash = info.path.indexOf('/');
6077                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
6078                        info.packageName = info.path.substring(0, slash);
6079                        info.path = info.path.substring(slash+1);
6080
6081                        // if it's a manifest or metadata payload we're done, otherwise parse
6082                        // out the domain into which the file will be restored
6083                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
6084                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
6085                            slash = info.path.indexOf('/');
6086                            if (slash < 0) {
6087                                throw new IOException("Illegal semantic path in non-manifest "
6088                                        + info.path);
6089                            }
6090                            info.domain = info.path.substring(0, slash);
6091                            info.path = info.path.substring(slash + 1);
6092                        }
6093                    }
6094                } catch (IOException e) {
6095                    if (DEBUG) {
6096                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
6097                        if (MORE_DEBUG) {
6098                            HEXLOG(block);
6099                        }
6100                    }
6101                    throw e;
6102                }
6103            }
6104            return info;
6105        }
6106
6107        private boolean isRestorableFile(FileMetadata info) {
6108            if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
6109                if (MORE_DEBUG) {
6110                    Slog.i(TAG, "Dropping cache file path " + info.path);
6111                }
6112                return false;
6113            }
6114
6115            if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
6116                // It's possible this is "no-backup" dir contents in an archive stream
6117                // produced on a device running a version of the OS that predates that
6118                // API.  Respect the no-backup intention and don't let the data get to
6119                // the app.
6120                if (info.path.startsWith("no_backup/")) {
6121                    if (MORE_DEBUG) {
6122                        Slog.i(TAG, "Dropping no_backup file path " + info.path);
6123                    }
6124                    return false;
6125                }
6126            }
6127
6128            // The path needs to be canonical
6129            if (info.path.contains("..") || info.path.contains("//")) {
6130                if (MORE_DEBUG) {
6131                    Slog.w(TAG, "Dropping invalid path " + info.path);
6132                }
6133                return false;
6134            }
6135
6136            // Otherwise we think this file is good to go
6137            return true;
6138        }
6139
6140        private void HEXLOG(byte[] block) {
6141            int offset = 0;
6142            int todo = block.length;
6143            StringBuilder buf = new StringBuilder(64);
6144            while (todo > 0) {
6145                buf.append(String.format("%04x   ", offset));
6146                int numThisLine = (todo > 16) ? 16 : todo;
6147                for (int i = 0; i < numThisLine; i++) {
6148                    buf.append(String.format("%02x ", block[offset+i]));
6149                }
6150                Slog.i("hexdump", buf.toString());
6151                buf.setLength(0);
6152                todo -= numThisLine;
6153                offset += numThisLine;
6154            }
6155        }
6156
6157        // Read exactly the given number of bytes into a buffer at the stated offset.
6158        // Returns false if EOF is encountered before the requested number of bytes
6159        // could be read.
6160        int readExactly(InputStream in, byte[] buffer, int offset, int size)
6161                throws IOException {
6162            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
6163if (MORE_DEBUG) Slog.i(TAG, "  ... readExactly(" + size + ") called");
6164            int soFar = 0;
6165            while (soFar < size) {
6166                int nRead = in.read(buffer, offset + soFar, size - soFar);
6167                if (nRead <= 0) {
6168                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
6169                    break;
6170                }
6171                soFar += nRead;
6172if (MORE_DEBUG) Slog.v(TAG, "   + got " + nRead + "; now wanting " + (size - soFar));
6173            }
6174            return soFar;
6175        }
6176
6177        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
6178            final int got = readExactly(instream, block, 0, 512);
6179            if (got == 0) return false;     // Clean EOF
6180            if (got < 512) throw new IOException("Unable to read full block header");
6181            mBytes += 512;
6182            return true;
6183        }
6184
6185        // overwrites 'info' fields based on the pax extended header
6186        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
6187                throws IOException {
6188            // We should never see a pax extended header larger than this
6189            if (info.size > 32*1024) {
6190                Slog.w(TAG, "Suspiciously large pax header size " + info.size
6191                        + " - aborting");
6192                throw new IOException("Sanity failure: pax header size " + info.size);
6193            }
6194
6195            // read whole blocks, not just the content size
6196            int numBlocks = (int)((info.size + 511) >> 9);
6197            byte[] data = new byte[numBlocks * 512];
6198            if (readExactly(instream, data, 0, data.length) < data.length) {
6199                throw new IOException("Unable to read full pax header");
6200            }
6201            mBytes += data.length;
6202
6203            final int contentSize = (int) info.size;
6204            int offset = 0;
6205            do {
6206                // extract the line at 'offset'
6207                int eol = offset+1;
6208                while (eol < contentSize && data[eol] != ' ') eol++;
6209                if (eol >= contentSize) {
6210                    // error: we just hit EOD looking for the end of the size field
6211                    throw new IOException("Invalid pax data");
6212                }
6213                // eol points to the space between the count and the key
6214                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
6215                int key = eol + 1;  // start of key=value
6216                eol = offset + linelen - 1; // trailing LF
6217                int value;
6218                for (value = key+1; data[value] != '=' && value <= eol; value++);
6219                if (value > eol) {
6220                    throw new IOException("Invalid pax declaration");
6221                }
6222
6223                // pax requires that key/value strings be in UTF-8
6224                String keyStr = new String(data, key, value-key, "UTF-8");
6225                // -1 to strip the trailing LF
6226                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
6227
6228                if ("path".equals(keyStr)) {
6229                    info.path = valStr;
6230                } else if ("size".equals(keyStr)) {
6231                    info.size = Long.parseLong(valStr);
6232                } else {
6233                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
6234                }
6235
6236                offset += linelen;
6237            } while (offset < contentSize);
6238
6239            return true;
6240        }
6241
6242        long extractRadix(byte[] data, int offset, int maxChars, int radix)
6243                throws IOException {
6244            long value = 0;
6245            final int end = offset + maxChars;
6246            for (int i = offset; i < end; i++) {
6247                final byte b = data[i];
6248                // Numeric fields in tar can terminate with either NUL or SPC
6249                if (b == 0 || b == ' ') break;
6250                if (b < '0' || b > ('0' + radix - 1)) {
6251                    throw new IOException("Invalid number in header: '" + (char)b
6252                            + "' for radix " + radix);
6253                }
6254                value = radix * value + (b - '0');
6255            }
6256            return value;
6257        }
6258
6259        String extractString(byte[] data, int offset, int maxChars) throws IOException {
6260            final int end = offset + maxChars;
6261            int eos = offset;
6262            // tar string fields terminate early with a NUL
6263            while (eos < end && data[eos] != 0) eos++;
6264            return new String(data, offset, eos-offset, "US-ASCII");
6265        }
6266
6267        void sendStartRestore() {
6268            if (mObserver != null) {
6269                try {
6270                    mObserver.onStartRestore();
6271                } catch (RemoteException e) {
6272                    Slog.w(TAG, "full restore observer went away: startRestore");
6273                    mObserver = null;
6274                }
6275            }
6276        }
6277
6278        void sendOnRestorePackage(String name) {
6279            if (mObserver != null) {
6280                try {
6281                    // TODO: use a more user-friendly name string
6282                    mObserver.onRestorePackage(name);
6283                } catch (RemoteException e) {
6284                    Slog.w(TAG, "full restore observer went away: restorePackage");
6285                    mObserver = null;
6286                }
6287            }
6288        }
6289
6290        void sendEndRestore() {
6291            if (mObserver != null) {
6292                try {
6293                    mObserver.onEndRestore();
6294                } catch (RemoteException e) {
6295                    Slog.w(TAG, "full restore observer went away: endRestore");
6296                    mObserver = null;
6297                }
6298            }
6299        }
6300    }
6301
6302    // ***** end new engine class ***
6303
6304    class PerformAdbRestoreTask implements Runnable {
6305        ParcelFileDescriptor mInputFile;
6306        String mCurrentPassword;
6307        String mDecryptPassword;
6308        IFullBackupRestoreObserver mObserver;
6309        AtomicBoolean mLatchObject;
6310        IBackupAgent mAgent;
6311        String mAgentPackage;
6312        ApplicationInfo mTargetApp;
6313        FullBackupObbConnection mObbConnection = null;
6314        ParcelFileDescriptor[] mPipes = null;
6315        byte[] mWidgetData = null;
6316
6317        long mBytes;
6318
6319        // possible handling states for a given package in the restore dataset
6320        final HashMap<String, RestorePolicy> mPackagePolicies
6321                = new HashMap<String, RestorePolicy>();
6322
6323        // installer package names for each encountered app, derived from the manifests
6324        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
6325
6326        // Signatures for a given package found in its manifest file
6327        final HashMap<String, Signature[]> mManifestSignatures
6328                = new HashMap<String, Signature[]>();
6329
6330        // Packages we've already wiped data on when restoring their first file
6331        final HashSet<String> mClearedPackages = new HashSet<String>();
6332
6333        PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
6334                IFullBackupRestoreObserver observer, AtomicBoolean latch) {
6335            mInputFile = fd;
6336            mCurrentPassword = curPassword;
6337            mDecryptPassword = decryptPassword;
6338            mObserver = observer;
6339            mLatchObject = latch;
6340            mAgent = null;
6341            mAgentPackage = null;
6342            mTargetApp = null;
6343            mObbConnection = new FullBackupObbConnection();
6344
6345            // Which packages we've already wiped data on.  We prepopulate this
6346            // with a whitelist of packages known to be unclearable.
6347            mClearedPackages.add("android");
6348            mClearedPackages.add(SETTINGS_PACKAGE);
6349        }
6350
6351        class RestoreFileRunnable implements Runnable {
6352            IBackupAgent mAgent;
6353            FileMetadata mInfo;
6354            ParcelFileDescriptor mSocket;
6355            int mToken;
6356
6357            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
6358                    ParcelFileDescriptor socket, int token) throws IOException {
6359                mAgent = agent;
6360                mInfo = info;
6361                mToken = token;
6362
6363                // This class is used strictly for process-local binder invocations.  The
6364                // semantics of ParcelFileDescriptor differ in this case; in particular, we
6365                // do not automatically get a 'dup'ed descriptor that we can can continue
6366                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
6367                // before proceeding to do the restore.
6368                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
6369            }
6370
6371            @Override
6372            public void run() {
6373                try {
6374                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
6375                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
6376                            mToken, mBackupManagerBinder);
6377                } catch (RemoteException e) {
6378                    // never happens; this is used strictly for local binder calls
6379                }
6380            }
6381        }
6382
6383        @Override
6384        public void run() {
6385            Slog.i(TAG, "--- Performing full-dataset restore ---");
6386            mObbConnection.establish();
6387            sendStartRestore();
6388
6389            // Are we able to restore shared-storage data?
6390            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
6391                mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
6392            }
6393
6394            FileInputStream rawInStream = null;
6395            DataInputStream rawDataIn = null;
6396            try {
6397                if (!backupPasswordMatches(mCurrentPassword)) {
6398                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
6399                    return;
6400                }
6401
6402                mBytes = 0;
6403                byte[] buffer = new byte[32 * 1024];
6404                rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
6405                rawDataIn = new DataInputStream(rawInStream);
6406
6407                // First, parse out the unencrypted/uncompressed header
6408                boolean compressed = false;
6409                InputStream preCompressStream = rawInStream;
6410                final InputStream in;
6411
6412                boolean okay = false;
6413                final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
6414                byte[] streamHeader = new byte[headerLen];
6415                rawDataIn.readFully(streamHeader);
6416                byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
6417                if (Arrays.equals(magicBytes, streamHeader)) {
6418                    // okay, header looks good.  now parse out the rest of the fields.
6419                    String s = readHeaderLine(rawInStream);
6420                    final int archiveVersion = Integer.parseInt(s);
6421                    if (archiveVersion <= BACKUP_FILE_VERSION) {
6422                        // okay, it's a version we recognize.  if it's version 1, we may need
6423                        // to try two different PBKDF2 regimes to compare checksums.
6424                        final boolean pbkdf2Fallback = (archiveVersion == 1);
6425
6426                        s = readHeaderLine(rawInStream);
6427                        compressed = (Integer.parseInt(s) != 0);
6428                        s = readHeaderLine(rawInStream);
6429                        if (s.equals("none")) {
6430                            // no more header to parse; we're good to go
6431                            okay = true;
6432                        } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
6433                            preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
6434                                    rawInStream);
6435                            if (preCompressStream != null) {
6436                                okay = true;
6437                            }
6438                        } else Slog.w(TAG, "Archive is encrypted but no password given");
6439                    } else Slog.w(TAG, "Wrong header version: " + s);
6440                } else Slog.w(TAG, "Didn't read the right header magic");
6441
6442                if (!okay) {
6443                    Slog.w(TAG, "Invalid restore data; aborting.");
6444                    return;
6445                }
6446
6447                // okay, use the right stream layer based on compression
6448                in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
6449
6450                boolean didRestore;
6451                do {
6452                    didRestore = restoreOneFile(in, buffer);
6453                } while (didRestore);
6454
6455                if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
6456            } catch (IOException e) {
6457                Slog.e(TAG, "Unable to read restore input");
6458            } finally {
6459                tearDownPipes();
6460                tearDownAgent(mTargetApp);
6461
6462                try {
6463                    if (rawDataIn != null) rawDataIn.close();
6464                    if (rawInStream != null) rawInStream.close();
6465                    mInputFile.close();
6466                } catch (IOException e) {
6467                    Slog.w(TAG, "Close of restore data pipe threw", e);
6468                    /* nothing we can do about this */
6469                }
6470                synchronized (mCurrentOpLock) {
6471                    mCurrentOperations.clear();
6472                }
6473                synchronized (mLatchObject) {
6474                    mLatchObject.set(true);
6475                    mLatchObject.notifyAll();
6476                }
6477                mObbConnection.tearDown();
6478                sendEndRestore();
6479                Slog.d(TAG, "Full restore pass complete.");
6480                mWakelock.release();
6481            }
6482        }
6483
6484        String readHeaderLine(InputStream in) throws IOException {
6485            int c;
6486            StringBuilder buffer = new StringBuilder(80);
6487            while ((c = in.read()) >= 0) {
6488                if (c == '\n') break;   // consume and discard the newlines
6489                buffer.append((char)c);
6490            }
6491            return buffer.toString();
6492        }
6493
6494        InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
6495                int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
6496                boolean doLog) {
6497            InputStream result = null;
6498
6499            try {
6500                Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
6501                SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt,
6502                        rounds);
6503                byte[] IV = hexToByteArray(userIvHex);
6504                IvParameterSpec ivSpec = new IvParameterSpec(IV);
6505                c.init(Cipher.DECRYPT_MODE,
6506                        new SecretKeySpec(userKey.getEncoded(), "AES"),
6507                        ivSpec);
6508                byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
6509                byte[] mkBlob = c.doFinal(mkCipher);
6510
6511                // first, the master key IV
6512                int offset = 0;
6513                int len = mkBlob[offset++];
6514                IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
6515                offset += len;
6516                // then the master key itself
6517                len = mkBlob[offset++];
6518                byte[] mk = Arrays.copyOfRange(mkBlob,
6519                        offset, offset + len);
6520                offset += len;
6521                // and finally the master key checksum hash
6522                len = mkBlob[offset++];
6523                byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
6524                        offset, offset + len);
6525
6526                // now validate the decrypted master key against the checksum
6527                byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds);
6528                if (Arrays.equals(calculatedCk, mkChecksum)) {
6529                    ivSpec = new IvParameterSpec(IV);
6530                    c.init(Cipher.DECRYPT_MODE,
6531                            new SecretKeySpec(mk, "AES"),
6532                            ivSpec);
6533                    // Only if all of the above worked properly will 'result' be assigned
6534                    result = new CipherInputStream(rawInStream, c);
6535                } else if (doLog) Slog.w(TAG, "Incorrect password");
6536            } catch (InvalidAlgorithmParameterException e) {
6537                if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e);
6538            } catch (BadPaddingException e) {
6539                // This case frequently occurs when the wrong password is used to decrypt
6540                // the master key.  Use the identical "incorrect password" log text as is
6541                // used in the checksum failure log in order to avoid providing additional
6542                // information to an attacker.
6543                if (doLog) Slog.w(TAG, "Incorrect password");
6544            } catch (IllegalBlockSizeException e) {
6545                if (doLog) Slog.w(TAG, "Invalid block size in master key");
6546            } catch (NoSuchAlgorithmException e) {
6547                if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!");
6548            } catch (NoSuchPaddingException e) {
6549                if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!");
6550            } catch (InvalidKeyException e) {
6551                if (doLog) Slog.w(TAG, "Illegal password; aborting");
6552            }
6553
6554            return result;
6555        }
6556
6557        InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
6558                InputStream rawInStream) {
6559            InputStream result = null;
6560            try {
6561                if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
6562
6563                    String userSaltHex = readHeaderLine(rawInStream); // 5
6564                    byte[] userSalt = hexToByteArray(userSaltHex);
6565
6566                    String ckSaltHex = readHeaderLine(rawInStream); // 6
6567                    byte[] ckSalt = hexToByteArray(ckSaltHex);
6568
6569                    int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
6570                    String userIvHex = readHeaderLine(rawInStream); // 8
6571
6572                    String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
6573
6574                    // decrypt the master key blob
6575                    result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt,
6576                            rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
6577                    if (result == null && pbkdf2Fallback) {
6578                        result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt,
6579                                rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
6580                    }
6581                } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
6582            } catch (NumberFormatException e) {
6583                Slog.w(TAG, "Can't parse restore data header");
6584            } catch (IOException e) {
6585                Slog.w(TAG, "Can't read input header");
6586            }
6587
6588            return result;
6589        }
6590
6591        boolean restoreOneFile(InputStream instream, byte[] buffer) {
6592            FileMetadata info;
6593            try {
6594                info = readTarHeaders(instream);
6595                if (info != null) {
6596                    if (MORE_DEBUG) {
6597                        dumpFileMetadata(info);
6598                    }
6599
6600                    final String pkg = info.packageName;
6601                    if (!pkg.equals(mAgentPackage)) {
6602                        // okay, change in package; set up our various
6603                        // bookkeeping if we haven't seen it yet
6604                        if (!mPackagePolicies.containsKey(pkg)) {
6605                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6606                        }
6607
6608                        // Clean up the previous agent relationship if necessary,
6609                        // and let the observer know we're considering a new app.
6610                        if (mAgent != null) {
6611                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
6612                            // Now we're really done
6613                            tearDownPipes();
6614                            tearDownAgent(mTargetApp);
6615                            mTargetApp = null;
6616                            mAgentPackage = null;
6617                        }
6618                    }
6619
6620                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
6621                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
6622                        mPackageInstallers.put(pkg, info.installerPackageName);
6623                        // We've read only the manifest content itself at this point,
6624                        // so consume the footer before looping around to the next
6625                        // input file
6626                        skipTarPadding(info.size, instream);
6627                        sendOnRestorePackage(pkg);
6628                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
6629                        // Metadata blobs!
6630                        readMetadata(info, instream);
6631                        skipTarPadding(info.size, instream);
6632                    } else {
6633                        // Non-manifest, so it's actual file data.  Is this a package
6634                        // we're ignoring?
6635                        boolean okay = true;
6636                        RestorePolicy policy = mPackagePolicies.get(pkg);
6637                        switch (policy) {
6638                            case IGNORE:
6639                                okay = false;
6640                                break;
6641
6642                            case ACCEPT_IF_APK:
6643                                // If we're in accept-if-apk state, then the first file we
6644                                // see MUST be the apk.
6645                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6646                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
6647                                    // Try to install the app.
6648                                    String installerName = mPackageInstallers.get(pkg);
6649                                    okay = installApk(info, installerName, instream);
6650                                    // good to go; promote to ACCEPT
6651                                    mPackagePolicies.put(pkg, (okay)
6652                                            ? RestorePolicy.ACCEPT
6653                                            : RestorePolicy.IGNORE);
6654                                    // At this point we've consumed this file entry
6655                                    // ourselves, so just strip the tar footer and
6656                                    // go on to the next file in the input stream
6657                                    skipTarPadding(info.size, instream);
6658                                    return true;
6659                                } else {
6660                                    // File data before (or without) the apk.  We can't
6661                                    // handle it coherently in this case so ignore it.
6662                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6663                                    okay = false;
6664                                }
6665                                break;
6666
6667                            case ACCEPT:
6668                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6669                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
6670                                    // we can take the data without the apk, so we
6671                                    // *want* to do so.  skip the apk by declaring this
6672                                    // one file not-okay without changing the restore
6673                                    // policy for the package.
6674                                    okay = false;
6675                                }
6676                                break;
6677
6678                            default:
6679                                // Something has gone dreadfully wrong when determining
6680                                // the restore policy from the manifest.  Ignore the
6681                                // rest of this package's data.
6682                                Slog.e(TAG, "Invalid policy from manifest");
6683                                okay = false;
6684                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6685                                break;
6686                        }
6687
6688                        // The path needs to be canonical
6689                        if (info.path.contains("..") || info.path.contains("//")) {
6690                            if (MORE_DEBUG) {
6691                                Slog.w(TAG, "Dropping invalid path " + info.path);
6692                            }
6693                            okay = false;
6694                        }
6695
6696                        // If the policy is satisfied, go ahead and set up to pipe the
6697                        // data to the agent.
6698                        if (DEBUG && okay && mAgent != null) {
6699                            Slog.i(TAG, "Reusing existing agent instance");
6700                        }
6701                        if (okay && mAgent == null) {
6702                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
6703
6704                            try {
6705                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
6706
6707                                // If we haven't sent any data to this app yet, we probably
6708                                // need to clear it first.  Check that.
6709                                if (!mClearedPackages.contains(pkg)) {
6710                                    // apps with their own backup agents are
6711                                    // responsible for coherently managing a full
6712                                    // restore.
6713                                    if (mTargetApp.backupAgentName == null) {
6714                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
6715                                        clearApplicationDataSynchronous(pkg);
6716                                    } else {
6717                                        if (DEBUG) Slog.d(TAG, "backup agent ("
6718                                                + mTargetApp.backupAgentName + ") => no clear");
6719                                    }
6720                                    mClearedPackages.add(pkg);
6721                                } else {
6722                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
6723                                }
6724
6725                                // All set; now set up the IPC and launch the agent
6726                                setUpPipes();
6727                                mAgent = bindToAgentSynchronous(mTargetApp,
6728                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
6729                                mAgentPackage = pkg;
6730                            } catch (IOException e) {
6731                                // fall through to error handling
6732                            } catch (NameNotFoundException e) {
6733                                // fall through to error handling
6734                            }
6735
6736                            if (mAgent == null) {
6737                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
6738                                okay = false;
6739                                tearDownPipes();
6740                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6741                            }
6742                        }
6743
6744                        // Sanity check: make sure we never give data to the wrong app.  This
6745                        // should never happen but a little paranoia here won't go amiss.
6746                        if (okay && !pkg.equals(mAgentPackage)) {
6747                            Slog.e(TAG, "Restoring data for " + pkg
6748                                    + " but agent is for " + mAgentPackage);
6749                            okay = false;
6750                        }
6751
6752                        // At this point we have an agent ready to handle the full
6753                        // restore data as well as a pipe for sending data to
6754                        // that agent.  Tell the agent to start reading from the
6755                        // pipe.
6756                        if (okay) {
6757                            boolean agentSuccess = true;
6758                            long toCopy = info.size;
6759                            final int token = generateToken();
6760                            try {
6761                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
6762                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
6763                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
6764                                            + " : " + info.path);
6765                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
6766                                            info.size, info.type, info.path, info.mode,
6767                                            info.mtime, token, mBackupManagerBinder);
6768                                } else {
6769                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
6770                                            + info.path);
6771                                    // fire up the app's agent listening on the socket.  If
6772                                    // the agent is running in the system process we can't
6773                                    // just invoke it asynchronously, so we provide a thread
6774                                    // for it here.
6775                                    if (mTargetApp.processName.equals("system")) {
6776                                        Slog.d(TAG, "system process agent - spinning a thread");
6777                                        RestoreFileRunnable runner = new RestoreFileRunnable(
6778                                                mAgent, info, mPipes[0], token);
6779                                        new Thread(runner, "restore-sys-runner").start();
6780                                    } else {
6781                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
6782                                                info.domain, info.path, info.mode, info.mtime,
6783                                                token, mBackupManagerBinder);
6784                                    }
6785                                }
6786                            } catch (IOException e) {
6787                                // couldn't dup the socket for a process-local restore
6788                                Slog.d(TAG, "Couldn't establish restore");
6789                                agentSuccess = false;
6790                                okay = false;
6791                            } catch (RemoteException e) {
6792                                // whoops, remote entity went away.  We'll eat the content
6793                                // ourselves, then, and not copy it over.
6794                                Slog.e(TAG, "Agent crashed during full restore");
6795                                agentSuccess = false;
6796                                okay = false;
6797                            }
6798
6799                            // Copy over the data if the agent is still good
6800                            if (okay) {
6801                                boolean pipeOkay = true;
6802                                FileOutputStream pipe = new FileOutputStream(
6803                                        mPipes[1].getFileDescriptor());
6804                                while (toCopy > 0) {
6805                                    int toRead = (toCopy > buffer.length)
6806                                    ? buffer.length : (int)toCopy;
6807                                    int nRead = instream.read(buffer, 0, toRead);
6808                                    if (nRead >= 0) mBytes += nRead;
6809                                    if (nRead <= 0) break;
6810                                    toCopy -= nRead;
6811
6812                                    // send it to the output pipe as long as things
6813                                    // are still good
6814                                    if (pipeOkay) {
6815                                        try {
6816                                            pipe.write(buffer, 0, nRead);
6817                                        } catch (IOException e) {
6818                                            Slog.e(TAG, "Failed to write to restore pipe", e);
6819                                            pipeOkay = false;
6820                                        }
6821                                    }
6822                                }
6823
6824                                // done sending that file!  Now we just need to consume
6825                                // the delta from info.size to the end of block.
6826                                skipTarPadding(info.size, instream);
6827
6828                                // and now that we've sent it all, wait for the remote
6829                                // side to acknowledge receipt
6830                                agentSuccess = waitUntilOperationComplete(token);
6831                            }
6832
6833                            // okay, if the remote end failed at any point, deal with
6834                            // it by ignoring the rest of the restore on it
6835                            if (!agentSuccess) {
6836                                mBackupHandler.removeMessages(MSG_TIMEOUT);
6837                                tearDownPipes();
6838                                tearDownAgent(mTargetApp);
6839                                mAgent = null;
6840                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6841                            }
6842                        }
6843
6844                        // Problems setting up the agent communication, or an already-
6845                        // ignored package: skip to the next tar stream entry by
6846                        // reading and discarding this file.
6847                        if (!okay) {
6848                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
6849                            long bytesToConsume = (info.size + 511) & ~511;
6850                            while (bytesToConsume > 0) {
6851                                int toRead = (bytesToConsume > buffer.length)
6852                                ? buffer.length : (int)bytesToConsume;
6853                                long nRead = instream.read(buffer, 0, toRead);
6854                                if (nRead >= 0) mBytes += nRead;
6855                                if (nRead <= 0) break;
6856                                bytesToConsume -= nRead;
6857                            }
6858                        }
6859                    }
6860                }
6861            } catch (IOException e) {
6862                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
6863                // treat as EOF
6864                info = null;
6865            }
6866
6867            return (info != null);
6868        }
6869
6870        void setUpPipes() throws IOException {
6871            mPipes = ParcelFileDescriptor.createPipe();
6872        }
6873
6874        void tearDownPipes() {
6875            if (mPipes != null) {
6876                try {
6877                    mPipes[0].close();
6878                    mPipes[0] = null;
6879                    mPipes[1].close();
6880                    mPipes[1] = null;
6881                } catch (IOException e) {
6882                    Slog.w(TAG, "Couldn't close agent pipes", e);
6883                }
6884                mPipes = null;
6885            }
6886        }
6887
6888        void tearDownAgent(ApplicationInfo app) {
6889            if (mAgent != null) {
6890                try {
6891                    // unbind and tidy up even on timeout or failure, just in case
6892                    mActivityManager.unbindBackupAgent(app);
6893
6894                    // The agent was running with a stub Application object, so shut it down.
6895                    // !!! We hardcode the confirmation UI's package name here rather than use a
6896                    //     manifest flag!  TODO something less direct.
6897                    if (app.uid >= Process.FIRST_APPLICATION_UID
6898                            && !app.packageName.equals("com.android.backupconfirm")) {
6899                        if (DEBUG) Slog.d(TAG, "Killing host process");
6900                        mActivityManager.killApplicationProcess(app.processName, app.uid);
6901                    } else {
6902                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
6903                    }
6904                } catch (RemoteException e) {
6905                    Slog.d(TAG, "Lost app trying to shut down");
6906                }
6907                mAgent = null;
6908            }
6909        }
6910
6911        class RestoreInstallObserver extends PackageInstallObserver {
6912            final AtomicBoolean mDone = new AtomicBoolean();
6913            String mPackageName;
6914            int mResult;
6915
6916            public void reset() {
6917                synchronized (mDone) {
6918                    mDone.set(false);
6919                }
6920            }
6921
6922            public void waitForCompletion() {
6923                synchronized (mDone) {
6924                    while (mDone.get() == false) {
6925                        try {
6926                            mDone.wait();
6927                        } catch (InterruptedException e) { }
6928                    }
6929                }
6930            }
6931
6932            int getResult() {
6933                return mResult;
6934            }
6935
6936            @Override
6937            public void onPackageInstalled(String packageName, int returnCode,
6938                    String msg, Bundle extras) {
6939                synchronized (mDone) {
6940                    mResult = returnCode;
6941                    mPackageName = packageName;
6942                    mDone.set(true);
6943                    mDone.notifyAll();
6944                }
6945            }
6946        }
6947
6948        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
6949            final AtomicBoolean mDone = new AtomicBoolean();
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            @Override
6969            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
6970                synchronized (mDone) {
6971                    mResult = returnCode;
6972                    mDone.set(true);
6973                    mDone.notifyAll();
6974                }
6975            }
6976        }
6977
6978        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
6979        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
6980
6981        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
6982            boolean okay = true;
6983
6984            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
6985
6986            // The file content is an .apk file.  Copy it out to a staging location and
6987            // attempt to install it.
6988            File apkFile = new File(mDataDir, info.packageName);
6989            try {
6990                FileOutputStream apkStream = new FileOutputStream(apkFile);
6991                byte[] buffer = new byte[32 * 1024];
6992                long size = info.size;
6993                while (size > 0) {
6994                    long toRead = (buffer.length < size) ? buffer.length : size;
6995                    int didRead = instream.read(buffer, 0, (int)toRead);
6996                    if (didRead >= 0) mBytes += didRead;
6997                    apkStream.write(buffer, 0, didRead);
6998                    size -= didRead;
6999                }
7000                apkStream.close();
7001
7002                // make sure the installer can read it
7003                apkFile.setReadable(true, false);
7004
7005                // Now install it
7006                Uri packageUri = Uri.fromFile(apkFile);
7007                mInstallObserver.reset();
7008                mPackageManager.installPackage(packageUri, mInstallObserver,
7009                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
7010                        installerPackage);
7011                mInstallObserver.waitForCompletion();
7012
7013                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
7014                    // The only time we continue to accept install of data even if the
7015                    // apk install failed is if we had already determined that we could
7016                    // accept the data regardless.
7017                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
7018                        okay = false;
7019                    }
7020                } else {
7021                    // Okay, the install succeeded.  Make sure it was the right app.
7022                    boolean uninstall = false;
7023                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
7024                        Slog.w(TAG, "Restore stream claimed to include apk for "
7025                                + info.packageName + " but apk was really "
7026                                + mInstallObserver.mPackageName);
7027                        // delete the package we just put in place; it might be fraudulent
7028                        okay = false;
7029                        uninstall = true;
7030                    } else {
7031                        try {
7032                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
7033                                    PackageManager.GET_SIGNATURES);
7034                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
7035                                Slog.w(TAG, "Restore stream contains apk of package "
7036                                        + info.packageName + " but it disallows backup/restore");
7037                                okay = false;
7038                            } else {
7039                                // So far so good -- do the signatures match the manifest?
7040                                Signature[] sigs = mManifestSignatures.get(info.packageName);
7041                                if (signaturesMatch(sigs, pkg)) {
7042                                    // If this is a system-uid app without a declared backup agent,
7043                                    // don't restore any of the file data.
7044                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
7045                                            && (pkg.applicationInfo.backupAgentName == null)) {
7046                                        Slog.w(TAG, "Installed app " + info.packageName
7047                                                + " has restricted uid and no agent");
7048                                        okay = false;
7049                                    }
7050                                } else {
7051                                    Slog.w(TAG, "Installed app " + info.packageName
7052                                            + " signatures do not match restore manifest");
7053                                    okay = false;
7054                                    uninstall = true;
7055                                }
7056                            }
7057                        } catch (NameNotFoundException e) {
7058                            Slog.w(TAG, "Install of package " + info.packageName
7059                                    + " succeeded but now not found");
7060                            okay = false;
7061                        }
7062                    }
7063
7064                    // If we're not okay at this point, we need to delete the package
7065                    // that we just installed.
7066                    if (uninstall) {
7067                        mDeleteObserver.reset();
7068                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
7069                                mDeleteObserver, 0);
7070                        mDeleteObserver.waitForCompletion();
7071                    }
7072                }
7073            } catch (IOException e) {
7074                Slog.e(TAG, "Unable to transcribe restored apk for install");
7075                okay = false;
7076            } finally {
7077                apkFile.delete();
7078            }
7079
7080            return okay;
7081        }
7082
7083        // Given an actual file content size, consume the post-content padding mandated
7084        // by the tar format.
7085        void skipTarPadding(long size, InputStream instream) throws IOException {
7086            long partial = (size + 512) % 512;
7087            if (partial > 0) {
7088                final int needed = 512 - (int)partial;
7089                byte[] buffer = new byte[needed];
7090                if (readExactly(instream, buffer, 0, needed) == needed) {
7091                    mBytes += needed;
7092                } else throw new IOException("Unexpected EOF in padding");
7093            }
7094        }
7095
7096        // Read a widget metadata file, returning the restored blob
7097        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
7098            // Fail on suspiciously large widget dump files
7099            if (info.size > 64 * 1024) {
7100                throw new IOException("Metadata too big; corrupt? size=" + info.size);
7101            }
7102
7103            byte[] buffer = new byte[(int) info.size];
7104            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
7105                mBytes += info.size;
7106            } else throw new IOException("Unexpected EOF in widget data");
7107
7108            String[] str = new String[1];
7109            int offset = extractLine(buffer, 0, str);
7110            int version = Integer.parseInt(str[0]);
7111            if (version == BACKUP_MANIFEST_VERSION) {
7112                offset = extractLine(buffer, offset, str);
7113                final String pkg = str[0];
7114                if (info.packageName.equals(pkg)) {
7115                    // Data checks out -- the rest of the buffer is a concatenation of
7116                    // binary blobs as described in the comment at writeAppWidgetData()
7117                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
7118                            offset, buffer.length - offset);
7119                    DataInputStream in = new DataInputStream(bin);
7120                    while (bin.available() > 0) {
7121                        int token = in.readInt();
7122                        int size = in.readInt();
7123                        if (size > 64 * 1024) {
7124                            throw new IOException("Datum "
7125                                    + Integer.toHexString(token)
7126                                    + " too big; corrupt? size=" + info.size);
7127                        }
7128                        switch (token) {
7129                            case BACKUP_WIDGET_METADATA_TOKEN:
7130                            {
7131                                if (MORE_DEBUG) {
7132                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
7133                                }
7134                                mWidgetData = new byte[size];
7135                                in.read(mWidgetData);
7136                                break;
7137                            }
7138                            default:
7139                            {
7140                                if (DEBUG) {
7141                                    Slog.i(TAG, "Ignoring metadata blob "
7142                                            + Integer.toHexString(token)
7143                                            + " for " + info.packageName);
7144                                }
7145                                in.skipBytes(size);
7146                                break;
7147                            }
7148                        }
7149                    }
7150                } else {
7151                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
7152                            + " but widget data for " + pkg);
7153                }
7154            } else {
7155                Slog.w(TAG, "Unsupported metadata version " + version);
7156            }
7157        }
7158
7159        // Returns a policy constant; takes a buffer arg to reduce memory churn
7160        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
7161                throws IOException {
7162            // Fail on suspiciously large manifest files
7163            if (info.size > 64 * 1024) {
7164                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
7165            }
7166
7167            byte[] buffer = new byte[(int) info.size];
7168            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
7169                mBytes += info.size;
7170            } else throw new IOException("Unexpected EOF in manifest");
7171
7172            RestorePolicy policy = RestorePolicy.IGNORE;
7173            String[] str = new String[1];
7174            int offset = 0;
7175
7176            try {
7177                offset = extractLine(buffer, offset, str);
7178                int version = Integer.parseInt(str[0]);
7179                if (version == BACKUP_MANIFEST_VERSION) {
7180                    offset = extractLine(buffer, offset, str);
7181                    String manifestPackage = str[0];
7182                    // TODO: handle <original-package>
7183                    if (manifestPackage.equals(info.packageName)) {
7184                        offset = extractLine(buffer, offset, str);
7185                        version = Integer.parseInt(str[0]);  // app version
7186                        offset = extractLine(buffer, offset, str);
7187                        // This is the platform version, which we don't use, but we parse it
7188                        // as a safety against corruption in the manifest.
7189                        Integer.parseInt(str[0]);
7190                        offset = extractLine(buffer, offset, str);
7191                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
7192                        offset = extractLine(buffer, offset, str);
7193                        boolean hasApk = str[0].equals("1");
7194                        offset = extractLine(buffer, offset, str);
7195                        int numSigs = Integer.parseInt(str[0]);
7196                        if (numSigs > 0) {
7197                            Signature[] sigs = new Signature[numSigs];
7198                            for (int i = 0; i < numSigs; i++) {
7199                                offset = extractLine(buffer, offset, str);
7200                                sigs[i] = new Signature(str[0]);
7201                            }
7202                            mManifestSignatures.put(info.packageName, sigs);
7203
7204                            // Okay, got the manifest info we need...
7205                            try {
7206                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
7207                                        info.packageName, PackageManager.GET_SIGNATURES);
7208                                // Fall through to IGNORE if the app explicitly disallows backup
7209                                final int flags = pkgInfo.applicationInfo.flags;
7210                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
7211                                    // Restore system-uid-space packages only if they have
7212                                    // defined a custom backup agent
7213                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
7214                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
7215                                        // Verify signatures against any installed version; if they
7216                                        // don't match, then we fall though and ignore the data.  The
7217                                        // signatureMatch() method explicitly ignores the signature
7218                                        // check for packages installed on the system partition, because
7219                                        // such packages are signed with the platform cert instead of
7220                                        // the app developer's cert, so they're different on every
7221                                        // device.
7222                                        if (signaturesMatch(sigs, pkgInfo)) {
7223                                            if (pkgInfo.versionCode >= version) {
7224                                                Slog.i(TAG, "Sig + version match; taking data");
7225                                                policy = RestorePolicy.ACCEPT;
7226                                            } else {
7227                                                // The data is from a newer version of the app than
7228                                                // is presently installed.  That means we can only
7229                                                // use it if the matching apk is also supplied.
7230                                                Slog.d(TAG, "Data version " + version
7231                                                        + " is newer than installed version "
7232                                                        + pkgInfo.versionCode + " - requiring apk");
7233                                                policy = RestorePolicy.ACCEPT_IF_APK;
7234                                            }
7235                                        } else {
7236                                            Slog.w(TAG, "Restore manifest signatures do not match "
7237                                                    + "installed application for " + info.packageName);
7238                                        }
7239                                    } else {
7240                                        Slog.w(TAG, "Package " + info.packageName
7241                                                + " is system level with no agent");
7242                                    }
7243                                } else {
7244                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
7245                                            + info.packageName + " but allowBackup=false");
7246                                }
7247                            } catch (NameNotFoundException e) {
7248                                // Okay, the target app isn't installed.  We can process
7249                                // the restore properly only if the dataset provides the
7250                                // apk file and we can successfully install it.
7251                                if (DEBUG) Slog.i(TAG, "Package " + info.packageName
7252                                        + " not installed; requiring apk in dataset");
7253                                policy = RestorePolicy.ACCEPT_IF_APK;
7254                            }
7255
7256                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
7257                                Slog.i(TAG, "Cannot restore package " + info.packageName
7258                                        + " without the matching .apk");
7259                            }
7260                        } else {
7261                            Slog.i(TAG, "Missing signature on backed-up package "
7262                                    + info.packageName);
7263                        }
7264                    } else {
7265                        Slog.i(TAG, "Expected package " + info.packageName
7266                                + " but restore manifest claims " + manifestPackage);
7267                    }
7268                } else {
7269                    Slog.i(TAG, "Unknown restore manifest version " + version
7270                            + " for package " + info.packageName);
7271                }
7272            } catch (NumberFormatException e) {
7273                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
7274            } catch (IllegalArgumentException e) {
7275                Slog.w(TAG, e.getMessage());
7276            }
7277
7278            return policy;
7279        }
7280
7281        // Builds a line from a byte buffer starting at 'offset', and returns
7282        // the index of the next unconsumed data in the buffer.
7283        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
7284            final int end = buffer.length;
7285            if (offset >= end) throw new IOException("Incomplete data");
7286
7287            int pos;
7288            for (pos = offset; pos < end; pos++) {
7289                byte c = buffer[pos];
7290                // at LF we declare end of line, and return the next char as the
7291                // starting point for the next time through
7292                if (c == '\n') {
7293                    break;
7294                }
7295            }
7296            outStr[0] = new String(buffer, offset, pos - offset);
7297            pos++;  // may be pointing an extra byte past the end but that's okay
7298            return pos;
7299        }
7300
7301        void dumpFileMetadata(FileMetadata info) {
7302            if (DEBUG) {
7303                StringBuilder b = new StringBuilder(128);
7304
7305                // mode string
7306                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
7307                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
7308                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
7309                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
7310                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
7311                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
7312                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
7313                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
7314                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
7315                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
7316                b.append(String.format(" %9d ", info.size));
7317
7318                Date stamp = new Date(info.mtime);
7319                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
7320
7321                b.append(info.packageName);
7322                b.append(" :: ");
7323                b.append(info.domain);
7324                b.append(" :: ");
7325                b.append(info.path);
7326
7327                Slog.i(TAG, b.toString());
7328            }
7329        }
7330        // Consume a tar file header block [sequence] and accumulate the relevant metadata
7331        FileMetadata readTarHeaders(InputStream instream) throws IOException {
7332            byte[] block = new byte[512];
7333            FileMetadata info = null;
7334
7335            boolean gotHeader = readTarHeader(instream, block);
7336            if (gotHeader) {
7337                try {
7338                    // okay, presume we're okay, and extract the various metadata
7339                    info = new FileMetadata();
7340                    info.size = extractRadix(block, 124, 12, 8);
7341                    info.mtime = extractRadix(block, 136, 12, 8);
7342                    info.mode = extractRadix(block, 100, 8, 8);
7343
7344                    info.path = extractString(block, 345, 155); // prefix
7345                    String path = extractString(block, 0, 100);
7346                    if (path.length() > 0) {
7347                        if (info.path.length() > 0) info.path += '/';
7348                        info.path += path;
7349                    }
7350
7351                    // tar link indicator field: 1 byte at offset 156 in the header.
7352                    int typeChar = block[156];
7353                    if (typeChar == 'x') {
7354                        // pax extended header, so we need to read that
7355                        gotHeader = readPaxExtendedHeader(instream, info);
7356                        if (gotHeader) {
7357                            // and after a pax extended header comes another real header -- read
7358                            // that to find the real file type
7359                            gotHeader = readTarHeader(instream, block);
7360                        }
7361                        if (!gotHeader) throw new IOException("Bad or missing pax header");
7362
7363                        typeChar = block[156];
7364                    }
7365
7366                    switch (typeChar) {
7367                        case '0': info.type = BackupAgent.TYPE_FILE; break;
7368                        case '5': {
7369                            info.type = BackupAgent.TYPE_DIRECTORY;
7370                            if (info.size != 0) {
7371                                Slog.w(TAG, "Directory entry with nonzero size in header");
7372                                info.size = 0;
7373                            }
7374                            break;
7375                        }
7376                        case 0: {
7377                            // presume EOF
7378                            if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
7379                            return null;
7380                        }
7381                        default: {
7382                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
7383                            throw new IOException("Unknown entity type " + typeChar);
7384                        }
7385                    }
7386
7387                    // Parse out the path
7388                    //
7389                    // first: apps/shared/unrecognized
7390                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
7391                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
7392                        // File in shared storage.  !!! TODO: implement this.
7393                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
7394                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
7395                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
7396                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
7397                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
7398                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
7399                        // App content!  Parse out the package name and domain
7400
7401                        // strip the apps/ prefix
7402                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
7403
7404                        // extract the package name
7405                        int slash = info.path.indexOf('/');
7406                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
7407                        info.packageName = info.path.substring(0, slash);
7408                        info.path = info.path.substring(slash+1);
7409
7410                        // if it's a manifest or metadata payload we're done, otherwise parse
7411                        // out the domain into which the file will be restored
7412                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
7413                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
7414                            slash = info.path.indexOf('/');
7415                            if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
7416                            info.domain = info.path.substring(0, slash);
7417                            info.path = info.path.substring(slash + 1);
7418                        }
7419                    }
7420                } catch (IOException e) {
7421                    if (DEBUG) {
7422                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
7423                        HEXLOG(block);
7424                    }
7425                    throw e;
7426                }
7427            }
7428            return info;
7429        }
7430
7431        private void HEXLOG(byte[] block) {
7432            int offset = 0;
7433            int todo = block.length;
7434            StringBuilder buf = new StringBuilder(64);
7435            while (todo > 0) {
7436                buf.append(String.format("%04x   ", offset));
7437                int numThisLine = (todo > 16) ? 16 : todo;
7438                for (int i = 0; i < numThisLine; i++) {
7439                    buf.append(String.format("%02x ", block[offset+i]));
7440                }
7441                Slog.i("hexdump", buf.toString());
7442                buf.setLength(0);
7443                todo -= numThisLine;
7444                offset += numThisLine;
7445            }
7446        }
7447
7448        // Read exactly the given number of bytes into a buffer at the stated offset.
7449        // Returns false if EOF is encountered before the requested number of bytes
7450        // could be read.
7451        int readExactly(InputStream in, byte[] buffer, int offset, int size)
7452                throws IOException {
7453            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
7454
7455            int soFar = 0;
7456            while (soFar < size) {
7457                int nRead = in.read(buffer, offset + soFar, size - soFar);
7458                if (nRead <= 0) {
7459                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
7460                    break;
7461                }
7462                soFar += nRead;
7463            }
7464            return soFar;
7465        }
7466
7467        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
7468            final int got = readExactly(instream, block, 0, 512);
7469            if (got == 0) return false;     // Clean EOF
7470            if (got < 512) throw new IOException("Unable to read full block header");
7471            mBytes += 512;
7472            return true;
7473        }
7474
7475        // overwrites 'info' fields based on the pax extended header
7476        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
7477                throws IOException {
7478            // We should never see a pax extended header larger than this
7479            if (info.size > 32*1024) {
7480                Slog.w(TAG, "Suspiciously large pax header size " + info.size
7481                        + " - aborting");
7482                throw new IOException("Sanity failure: pax header size " + info.size);
7483            }
7484
7485            // read whole blocks, not just the content size
7486            int numBlocks = (int)((info.size + 511) >> 9);
7487            byte[] data = new byte[numBlocks * 512];
7488            if (readExactly(instream, data, 0, data.length) < data.length) {
7489                throw new IOException("Unable to read full pax header");
7490            }
7491            mBytes += data.length;
7492
7493            final int contentSize = (int) info.size;
7494            int offset = 0;
7495            do {
7496                // extract the line at 'offset'
7497                int eol = offset+1;
7498                while (eol < contentSize && data[eol] != ' ') eol++;
7499                if (eol >= contentSize) {
7500                    // error: we just hit EOD looking for the end of the size field
7501                    throw new IOException("Invalid pax data");
7502                }
7503                // eol points to the space between the count and the key
7504                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
7505                int key = eol + 1;  // start of key=value
7506                eol = offset + linelen - 1; // trailing LF
7507                int value;
7508                for (value = key+1; data[value] != '=' && value <= eol; value++);
7509                if (value > eol) {
7510                    throw new IOException("Invalid pax declaration");
7511                }
7512
7513                // pax requires that key/value strings be in UTF-8
7514                String keyStr = new String(data, key, value-key, "UTF-8");
7515                // -1 to strip the trailing LF
7516                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
7517
7518                if ("path".equals(keyStr)) {
7519                    info.path = valStr;
7520                } else if ("size".equals(keyStr)) {
7521                    info.size = Long.parseLong(valStr);
7522                } else {
7523                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
7524                }
7525
7526                offset += linelen;
7527            } while (offset < contentSize);
7528
7529            return true;
7530        }
7531
7532        long extractRadix(byte[] data, int offset, int maxChars, int radix)
7533                throws IOException {
7534            long value = 0;
7535            final int end = offset + maxChars;
7536            for (int i = offset; i < end; i++) {
7537                final byte b = data[i];
7538                // Numeric fields in tar can terminate with either NUL or SPC
7539                if (b == 0 || b == ' ') break;
7540                if (b < '0' || b > ('0' + radix - 1)) {
7541                    throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix);
7542                }
7543                value = radix * value + (b - '0');
7544            }
7545            return value;
7546        }
7547
7548        String extractString(byte[] data, int offset, int maxChars) throws IOException {
7549            final int end = offset + maxChars;
7550            int eos = offset;
7551            // tar string fields terminate early with a NUL
7552            while (eos < end && data[eos] != 0) eos++;
7553            return new String(data, offset, eos-offset, "US-ASCII");
7554        }
7555
7556        void sendStartRestore() {
7557            if (mObserver != null) {
7558                try {
7559                    mObserver.onStartRestore();
7560                } catch (RemoteException e) {
7561                    Slog.w(TAG, "full restore observer went away: startRestore");
7562                    mObserver = null;
7563                }
7564            }
7565        }
7566
7567        void sendOnRestorePackage(String name) {
7568            if (mObserver != null) {
7569                try {
7570                    // TODO: use a more user-friendly name string
7571                    mObserver.onRestorePackage(name);
7572                } catch (RemoteException e) {
7573                    Slog.w(TAG, "full restore observer went away: restorePackage");
7574                    mObserver = null;
7575                }
7576            }
7577        }
7578
7579        void sendEndRestore() {
7580            if (mObserver != null) {
7581                try {
7582                    mObserver.onEndRestore();
7583                } catch (RemoteException e) {
7584                    Slog.w(TAG, "full restore observer went away: endRestore");
7585                    mObserver = null;
7586                }
7587            }
7588        }
7589    }
7590
7591    // ----- Restore handling -----
7592
7593    // Old style: directly match the stored vs on device signature blocks
7594    static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
7595        if (target == null) {
7596            return false;
7597        }
7598
7599        // If the target resides on the system partition, we allow it to restore
7600        // data from the like-named package in a restore set even if the signatures
7601        // do not match.  (Unlike general applications, those flashed to the system
7602        // partition will be signed with the device's platform certificate, so on
7603        // different phones the same system app will have different signatures.)
7604        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
7605            if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
7606            return true;
7607        }
7608
7609        // Allow unsigned apps, but not signed on one device and unsigned on the other
7610        // !!! TODO: is this the right policy?
7611        Signature[] deviceSigs = target.signatures;
7612        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
7613                + " device=" + deviceSigs);
7614        if ((storedSigs == null || storedSigs.length == 0)
7615                && (deviceSigs == null || deviceSigs.length == 0)) {
7616            return true;
7617        }
7618        if (storedSigs == null || deviceSigs == null) {
7619            return false;
7620        }
7621
7622        // !!! TODO: this demands that every stored signature match one
7623        // that is present on device, and does not demand the converse.
7624        // Is this this right policy?
7625        int nStored = storedSigs.length;
7626        int nDevice = deviceSigs.length;
7627
7628        for (int i=0; i < nStored; i++) {
7629            boolean match = false;
7630            for (int j=0; j < nDevice; j++) {
7631                if (storedSigs[i].equals(deviceSigs[j])) {
7632                    match = true;
7633                    break;
7634                }
7635            }
7636            if (!match) {
7637                return false;
7638            }
7639        }
7640        return true;
7641    }
7642
7643    // Used by both incremental and full restore
7644    void restoreWidgetData(String packageName, byte[] widgetData) {
7645        // Apply the restored widget state and generate the ID update for the app
7646        // TODO: http://b/22388012
7647        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
7648    }
7649
7650    // *****************************
7651    // NEW UNIFIED RESTORE IMPLEMENTATION
7652    // *****************************
7653
7654    // states of the unified-restore state machine
7655    enum UnifiedRestoreState {
7656        INITIAL,
7657        RUNNING_QUEUE,
7658        RESTORE_KEYVALUE,
7659        RESTORE_FULL,
7660        RESTORE_FINISHED,
7661        FINAL
7662    }
7663
7664    class PerformUnifiedRestoreTask implements BackupRestoreTask {
7665        // Transport we're working with to do the restore
7666        private IBackupTransport mTransport;
7667
7668        // Where per-transport saved state goes
7669        File mStateDir;
7670
7671        // Restore observer; may be null
7672        private IRestoreObserver mObserver;
7673
7674        // Token identifying the dataset to the transport
7675        private long mToken;
7676
7677        // When this is a restore-during-install, this is the token identifying the
7678        // operation to the Package Manager, and we must ensure that we let it know
7679        // when we're finished.
7680        private int mPmToken;
7681
7682        // Is this a whole-system restore, i.e. are we establishing a new ancestral
7683        // dataset to base future restore-at-install operations from?
7684        private boolean mIsSystemRestore;
7685
7686        // If this is a single-package restore, what package are we interested in?
7687        private PackageInfo mTargetPackage;
7688
7689        // In all cases, the calculated list of packages that we are trying to restore
7690        private List<PackageInfo> mAcceptSet;
7691
7692        // Our bookkeeping about the ancestral dataset
7693        private PackageManagerBackupAgent mPmAgent;
7694
7695        // Currently-bound backup agent for restore + restoreFinished purposes
7696        private IBackupAgent mAgent;
7697
7698        // What sort of restore we're doing now
7699        private RestoreDescription mRestoreDescription;
7700
7701        // The package we're currently restoring
7702        private PackageInfo mCurrentPackage;
7703
7704        // Widget-related data handled as part of this restore operation
7705        private byte[] mWidgetData;
7706
7707        // Number of apps restored in this pass
7708        private int mCount;
7709
7710        // When did we start?
7711        private long mStartRealtime;
7712
7713        // State machine progress
7714        private UnifiedRestoreState mState;
7715
7716        // How are things going?
7717        private int mStatus;
7718
7719        // Done?
7720        private boolean mFinished;
7721
7722        // Key/value: bookkeeping about staged data and files for agent access
7723        private File mBackupDataName;
7724        private File mStageName;
7725        private File mSavedStateName;
7726        private File mNewStateName;
7727        ParcelFileDescriptor mBackupData;
7728        ParcelFileDescriptor mNewState;
7729
7730        // Invariant: mWakelock is already held, and this task is responsible for
7731        // releasing it at the end of the restore operation.
7732        PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer,
7733                long restoreSetToken, PackageInfo targetPackage, int pmToken,
7734                boolean isFullSystemRestore, String[] filterSet) {
7735            mState = UnifiedRestoreState.INITIAL;
7736            mStartRealtime = SystemClock.elapsedRealtime();
7737
7738            mTransport = transport;
7739            mObserver = observer;
7740            mToken = restoreSetToken;
7741            mPmToken = pmToken;
7742            mTargetPackage = targetPackage;
7743            mIsSystemRestore = isFullSystemRestore;
7744            mFinished = false;
7745
7746            if (targetPackage != null) {
7747                // Single package restore
7748                mAcceptSet = new ArrayList<PackageInfo>();
7749                mAcceptSet.add(targetPackage);
7750            } else {
7751                // Everything possible, or a target set
7752                if (filterSet == null) {
7753                    // We want everything and a pony
7754                    List<PackageInfo> apps =
7755                            PackageManagerBackupAgent.getStorableApplications(mPackageManager);
7756                    filterSet = packagesToNames(apps);
7757                    if (DEBUG) {
7758                        Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
7759                    }
7760                }
7761
7762                mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
7763
7764                // Pro tem, we insist on moving the settings provider package to last place.
7765                // Keep track of whether it's in the list, and bump it down if so.  We also
7766                // want to do the system package itself first if it's called for.
7767                boolean hasSystem = false;
7768                boolean hasSettings = false;
7769                for (int i = 0; i < filterSet.length; i++) {
7770                    try {
7771                        PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
7772                        if ("android".equals(info.packageName)) {
7773                            hasSystem = true;
7774                            continue;
7775                        }
7776                        if (SETTINGS_PACKAGE.equals(info.packageName)) {
7777                            hasSettings = true;
7778                            continue;
7779                        }
7780
7781                        if (appIsEligibleForBackup(info.applicationInfo)) {
7782                            mAcceptSet.add(info);
7783                        }
7784                    } catch (NameNotFoundException e) {
7785                        // requested package name doesn't exist; ignore it
7786                    }
7787                }
7788                if (hasSystem) {
7789                    try {
7790                        mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
7791                    } catch (NameNotFoundException e) {
7792                        // won't happen; we know a priori that it's valid
7793                    }
7794                }
7795                if (hasSettings) {
7796                    try {
7797                        mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
7798                    } catch (NameNotFoundException e) {
7799                        // this one is always valid too
7800                    }
7801                }
7802            }
7803
7804            if (MORE_DEBUG) {
7805                Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
7806                for (PackageInfo info : mAcceptSet) {
7807                    Slog.v(TAG, "   " + info.packageName);
7808                }
7809            }
7810        }
7811
7812        private String[] packagesToNames(List<PackageInfo> apps) {
7813            final int N = apps.size();
7814            String[] names = new String[N];
7815            for (int i = 0; i < N; i++) {
7816                names[i] = apps.get(i).packageName;
7817            }
7818            return names;
7819        }
7820
7821        // Execute one tick of whatever state machine the task implements
7822        @Override
7823        public void execute() {
7824            if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState);
7825            switch (mState) {
7826                case INITIAL:
7827                    startRestore();
7828                    break;
7829
7830                case RUNNING_QUEUE:
7831                    dispatchNextRestore();
7832                    break;
7833
7834                case RESTORE_KEYVALUE:
7835                    restoreKeyValue();
7836                    break;
7837
7838                case RESTORE_FULL:
7839                    restoreFull();
7840                    break;
7841
7842                case RESTORE_FINISHED:
7843                    restoreFinished();
7844                    break;
7845
7846                case FINAL:
7847                    if (!mFinished) finalizeRestore();
7848                    else {
7849                        Slog.e(TAG, "Duplicate finish");
7850                    }
7851                    mFinished = true;
7852                    break;
7853            }
7854        }
7855
7856        /*
7857         * SKETCH OF OPERATION
7858         *
7859         * create one of these PerformUnifiedRestoreTask objects, telling it which
7860         * dataset & transport to address, and then parameters within the restore
7861         * operation: single target package vs many, etc.
7862         *
7863         * 1. transport.startRestore(token, list-of-packages).  If we need @pm@  it is
7864         * always placed first and the settings provider always placed last [for now].
7865         *
7866         * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline]
7867         *
7868         *   [ state change => RUNNING_QUEUE ]
7869         *
7870         * NOW ITERATE:
7871         *
7872         * { 3. t.nextRestorePackage()
7873         *   4. does the metadata for this package allow us to restore it?
7874         *      does the on-disk app permit us to restore it? [re-check allowBackup etc]
7875         *   5. is this a key/value dataset?  => key/value agent restore
7876         *       [ state change => RESTORE_KEYVALUE ]
7877         *       5a. spin up agent
7878         *       5b. t.getRestoreData() to stage it properly
7879         *       5c. call into agent to perform restore
7880         *       5d. tear down agent
7881         *       [ state change => RUNNING_QUEUE ]
7882         *
7883         *   6. else it's a stream dataset:
7884         *       [ state change => RESTORE_FULL ]
7885         *       6a. instantiate the engine for a stream restore: engine handles agent lifecycles
7886         *       6b. spin off engine runner on separate thread
7887         *       6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket
7888         *       [ state change => RUNNING_QUEUE ]
7889         * }
7890         *
7891         *   [ state change => FINAL ]
7892         *
7893         * 7. t.finishRestore(), release wakelock, etc.
7894         *
7895         *
7896         */
7897
7898        // state INITIAL : set up for the restore and read the metadata if necessary
7899        private  void startRestore() {
7900            sendStartRestore(mAcceptSet.size());
7901
7902            // If we're starting a full-system restore, set up to begin widget ID remapping
7903            if (mIsSystemRestore) {
7904                // TODO: http://b/22388012
7905                AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM);
7906            }
7907
7908            try {
7909                String transportDir = mTransport.transportDirName();
7910                mStateDir = new File(mBaseStateDir, transportDir);
7911
7912                // Fetch the current metadata from the dataset first
7913                PackageInfo pmPackage = new PackageInfo();
7914                pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7915                mAcceptSet.add(0, pmPackage);
7916
7917                PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
7918                mStatus = mTransport.startRestore(mToken, packages);
7919                if (mStatus != BackupTransport.TRANSPORT_OK) {
7920                    Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
7921                    mStatus = BackupTransport.TRANSPORT_ERROR;
7922                    executeNextState(UnifiedRestoreState.FINAL);
7923                    return;
7924                }
7925
7926                RestoreDescription desc = mTransport.nextRestorePackage();
7927                if (desc == null) {
7928                    Slog.e(TAG, "No restore metadata available; halting");
7929                    mStatus = BackupTransport.TRANSPORT_ERROR;
7930                    executeNextState(UnifiedRestoreState.FINAL);
7931                    return;
7932                }
7933                if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
7934                    Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
7935                    mStatus = BackupTransport.TRANSPORT_ERROR;
7936                    executeNextState(UnifiedRestoreState.FINAL);
7937                    return;
7938                }
7939
7940                // Pull the Package Manager metadata from the restore set first
7941                mCurrentPackage = new PackageInfo();
7942                mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7943                mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
7944                mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
7945                if (MORE_DEBUG) {
7946                    Slog.v(TAG, "initiating restore for PMBA");
7947                }
7948                initiateOneRestore(mCurrentPackage, 0);
7949                // The PM agent called operationComplete() already, because our invocation
7950                // of it is process-local and therefore synchronous.  That means that the
7951                // next-state message (RUNNING_QUEUE) is already enqueued.  Only if we're
7952                // unable to proceed with running the queue do we remove that pending
7953                // message and jump straight to the FINAL state.  Because this was
7954                // synchronous we also know that we should cancel the pending timeout
7955                // message.
7956                mBackupHandler.removeMessages(MSG_TIMEOUT);
7957
7958                // Verify that the backup set includes metadata.  If not, we can't do
7959                // signature/version verification etc, so we simply do not proceed with
7960                // the restore operation.
7961                if (!mPmAgent.hasMetadata()) {
7962                    Slog.e(TAG, "No restore metadata available, so not restoring");
7963                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7964                            PACKAGE_MANAGER_SENTINEL,
7965                            "Package manager restore metadata missing");
7966                    mStatus = BackupTransport.TRANSPORT_ERROR;
7967                    mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7968                    executeNextState(UnifiedRestoreState.FINAL);
7969                    return;
7970                }
7971
7972                // Success; cache the metadata and continue as expected with the
7973                // next state already enqueued
7974
7975            } catch (RemoteException e) {
7976                // If we lost the transport at any time, halt
7977                Slog.e(TAG, "Unable to contact transport for restore");
7978                mStatus = BackupTransport.TRANSPORT_ERROR;
7979                mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7980                executeNextState(UnifiedRestoreState.FINAL);
7981                return;
7982            }
7983        }
7984
7985        // state RUNNING_QUEUE : figure out what the next thing to be restored is,
7986        // and fire the appropriate next step
7987        private void dispatchNextRestore() {
7988            UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
7989            try {
7990                mRestoreDescription = mTransport.nextRestorePackage();
7991                final String pkgName = (mRestoreDescription != null)
7992                        ? mRestoreDescription.getPackageName() : null;
7993                if (pkgName == null) {
7994                    Slog.e(TAG, "Failure getting next package name");
7995                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7996                    nextState = UnifiedRestoreState.FINAL;
7997                    return;
7998                } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) {
7999                    // Yay we've reached the end cleanly
8000                    if (DEBUG) {
8001                        Slog.v(TAG, "No more packages; finishing restore");
8002                    }
8003                    int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
8004                    EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
8005                    nextState = UnifiedRestoreState.FINAL;
8006                    return;
8007                }
8008
8009                if (DEBUG) {
8010                    Slog.i(TAG, "Next restore package: " + mRestoreDescription);
8011                }
8012                sendOnRestorePackage(pkgName);
8013
8014                Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName);
8015                if (metaInfo == null) {
8016                    Slog.e(TAG, "No metadata for " + pkgName);
8017                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
8018                            "Package metadata missing");
8019                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8020                    return;
8021                }
8022
8023                try {
8024                    mCurrentPackage = mPackageManager.getPackageInfo(
8025                            pkgName, PackageManager.GET_SIGNATURES);
8026                } catch (NameNotFoundException e) {
8027                    // Whoops, we thought we could restore this package but it
8028                    // turns out not to be present.  Skip it.
8029                    Slog.e(TAG, "Package not present: " + pkgName);
8030                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
8031                            "Package missing on device");
8032                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8033                    return;
8034                }
8035
8036                if (metaInfo.versionCode > mCurrentPackage.versionCode) {
8037                    // Data is from a "newer" version of the app than we have currently
8038                    // installed.  If the app has not declared that it is prepared to
8039                    // handle this case, we do not attempt the restore.
8040                    if ((mCurrentPackage.applicationInfo.flags
8041                            & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
8042                        String message = "Version " + metaInfo.versionCode
8043                                + " > installed version " + mCurrentPackage.versionCode;
8044                        Slog.w(TAG, "Package " + pkgName + ": " + message);
8045                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8046                                pkgName, message);
8047                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
8048                        return;
8049                    } else {
8050                        if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
8051                                + " > installed " + mCurrentPackage.versionCode
8052                                + " but restoreAnyVersion");
8053                    }
8054                }
8055
8056                if (MORE_DEBUG) Slog.v(TAG, "Package " + pkgName
8057                        + " restore version [" + metaInfo.versionCode
8058                        + "] is compatible with installed version ["
8059                        + mCurrentPackage.versionCode + "]");
8060
8061                // Reset per-package preconditions and fire the appropriate next state
8062                mWidgetData = null;
8063                final int type = mRestoreDescription.getDataType();
8064                if (type == RestoreDescription.TYPE_KEY_VALUE) {
8065                    nextState = UnifiedRestoreState.RESTORE_KEYVALUE;
8066                } else if (type == RestoreDescription.TYPE_FULL_STREAM) {
8067                    nextState = UnifiedRestoreState.RESTORE_FULL;
8068                } else {
8069                    // Unknown restore type; ignore this package and move on
8070                    Slog.e(TAG, "Unrecognized restore type " + type);
8071                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8072                    return;
8073                }
8074            } catch (RemoteException e) {
8075                Slog.e(TAG, "Can't get next target from transport; ending restore");
8076                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8077                nextState = UnifiedRestoreState.FINAL;
8078                return;
8079            } finally {
8080                executeNextState(nextState);
8081            }
8082        }
8083
8084        // state RESTORE_KEYVALUE : restore one package via key/value API set
8085        private void restoreKeyValue() {
8086            // Initiating the restore will pass responsibility for the state machine's
8087            // progress to the agent callback, so we do not always execute the
8088            // next state here.
8089            final String packageName = mCurrentPackage.packageName;
8090            // Validate some semantic requirements that apply in this way
8091            // only to the key/value restore API flow
8092            if (mCurrentPackage.applicationInfo.backupAgentName == null
8093                    || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
8094                if (MORE_DEBUG) {
8095                    Slog.i(TAG, "Data exists for package " + packageName
8096                            + " but app has no agent; skipping");
8097                }
8098                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8099                        "Package has no agent");
8100                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8101                return;
8102            }
8103
8104            Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
8105            if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
8106                Slog.w(TAG, "Signature mismatch restoring " + packageName);
8107                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8108                        "Signature mismatch");
8109                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8110                return;
8111            }
8112
8113            // Good to go!  Set up and bind the agent...
8114            mAgent = bindToAgentSynchronous(
8115                    mCurrentPackage.applicationInfo,
8116                    IApplicationThread.BACKUP_MODE_INCREMENTAL);
8117            if (mAgent == null) {
8118                Slog.w(TAG, "Can't find backup agent for " + packageName);
8119                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8120                        "Restore agent missing");
8121                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8122                return;
8123            }
8124
8125            // And then finally start the restore on this agent
8126            try {
8127                initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
8128                ++mCount;
8129            } catch (Exception e) {
8130                Slog.e(TAG, "Error when attempting restore: " + e.toString());
8131                keyValueAgentErrorCleanup();
8132                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8133            }
8134        }
8135
8136        // Guts of a key/value restore operation
8137        void initiateOneRestore(PackageInfo app, int appVersionCode) {
8138            final String packageName = app.packageName;
8139
8140            if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
8141
8142            // !!! TODO: get the dirs from the transport
8143            mBackupDataName = new File(mDataDir, packageName + ".restore");
8144            mStageName = new File(mDataDir, packageName + ".stage");
8145            mNewStateName = new File(mStateDir, packageName + ".new");
8146            mSavedStateName = new File(mStateDir, packageName);
8147
8148            // don't stage the 'android' package where the wallpaper data lives.  this is
8149            // an optimization: we know there's no widget data hosted/published by that
8150            // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
8151            // data following the download.
8152            boolean staging = !packageName.equals("android");
8153            ParcelFileDescriptor stage;
8154            File downloadFile = (staging) ? mStageName : mBackupDataName;
8155
8156            final int token = generateToken();
8157            try {
8158                // Run the transport's restore pass
8159                stage = ParcelFileDescriptor.open(downloadFile,
8160                        ParcelFileDescriptor.MODE_READ_WRITE |
8161                        ParcelFileDescriptor.MODE_CREATE |
8162                        ParcelFileDescriptor.MODE_TRUNCATE);
8163
8164                if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
8165                    // Transport-level failure, so we wind everything up and
8166                    // terminate the restore operation.
8167                    Slog.e(TAG, "Error getting restore data for " + packageName);
8168                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8169                    stage.close();
8170                    downloadFile.delete();
8171                    executeNextState(UnifiedRestoreState.FINAL);
8172                    return;
8173                }
8174
8175                // We have the data from the transport. Now we extract and strip
8176                // any per-package metadata (typically widget-related information)
8177                // if appropriate
8178                if (staging) {
8179                    stage.close();
8180                    stage = ParcelFileDescriptor.open(downloadFile,
8181                            ParcelFileDescriptor.MODE_READ_ONLY);
8182
8183                    mBackupData = ParcelFileDescriptor.open(mBackupDataName,
8184                            ParcelFileDescriptor.MODE_READ_WRITE |
8185                            ParcelFileDescriptor.MODE_CREATE |
8186                            ParcelFileDescriptor.MODE_TRUNCATE);
8187
8188                    BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
8189                    BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
8190                    byte[] buffer = new byte[8192]; // will grow when needed
8191                    while (in.readNextHeader()) {
8192                        final String key = in.getKey();
8193                        final int size = in.getDataSize();
8194
8195                        // is this a special key?
8196                        if (key.equals(KEY_WIDGET_STATE)) {
8197                            if (DEBUG) {
8198                                Slog.i(TAG, "Restoring widget state for " + packageName);
8199                            }
8200                            mWidgetData = new byte[size];
8201                            in.readEntityData(mWidgetData, 0, size);
8202                        } else {
8203                            if (size > buffer.length) {
8204                                buffer = new byte[size];
8205                            }
8206                            in.readEntityData(buffer, 0, size);
8207                            out.writeEntityHeader(key, size);
8208                            out.writeEntityData(buffer, size);
8209                        }
8210                    }
8211
8212                    mBackupData.close();
8213                }
8214
8215                // Okay, we have the data.  Now have the agent do the restore.
8216                stage.close();
8217
8218                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
8219                        ParcelFileDescriptor.MODE_READ_ONLY);
8220
8221                mNewState = ParcelFileDescriptor.open(mNewStateName,
8222                        ParcelFileDescriptor.MODE_READ_WRITE |
8223                        ParcelFileDescriptor.MODE_CREATE |
8224                        ParcelFileDescriptor.MODE_TRUNCATE);
8225
8226                // Kick off the restore, checking for hung agents.  The timeout or
8227                // the operationComplete() callback will schedule the next step,
8228                // so we do not do that here.
8229                prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
8230                mAgent.doRestore(mBackupData, appVersionCode, mNewState,
8231                        token, mBackupManagerBinder);
8232            } catch (Exception e) {
8233                Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
8234                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8235                        packageName, e.toString());
8236                keyValueAgentErrorCleanup();    // clears any pending timeout messages as well
8237
8238                // After a restore failure we go back to running the queue.  If there
8239                // are no more packages to be restored that will be handled by the
8240                // next step.
8241                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8242            }
8243        }
8244
8245        // state RESTORE_FULL : restore one package via streaming engine
8246        private void restoreFull() {
8247            // None of this can run on the work looper here, so we spin asynchronous
8248            // work like this:
8249            //
8250            //   StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk()
8251            //                       write it into the pipe to the engine
8252            //   EngineThread: FullRestoreEngine thread communicating with the target app
8253            //
8254            // When finished, StreamFeederThread executes next state as appropriate on the
8255            // backup looper, and the overall unified restore task resumes
8256            try {
8257                StreamFeederThread feeder = new StreamFeederThread();
8258                if (MORE_DEBUG) {
8259                    Slog.i(TAG, "Spinning threads for stream restore of "
8260                            + mCurrentPackage.packageName);
8261                }
8262                new Thread(feeder, "unified-stream-feeder").start();
8263
8264                // At this point the feeder is responsible for advancing the restore
8265                // state, so we're done here.
8266            } catch (IOException e) {
8267                // Unable to instantiate the feeder thread -- we need to bail on the
8268                // current target.  We haven't asked the transport for data yet, though,
8269                // so we can do that simply by going back to running the restore queue.
8270                Slog.e(TAG, "Unable to construct pipes for stream restore!");
8271                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8272            }
8273        }
8274
8275        // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
8276        private void restoreFinished() {
8277            try {
8278                final int token = generateToken();
8279                prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
8280                mAgent.doRestoreFinished(token, mBackupManagerBinder);
8281                // If we get this far, the callback or timeout will schedule the
8282                // next restore state, so we're done
8283            } catch (Exception e) {
8284                final String packageName = mCurrentPackage.packageName;
8285                Slog.e(TAG, "Unable to finalize restore of " + packageName);
8286                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8287                        packageName, e.toString());
8288                keyValueAgentErrorCleanup();
8289                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8290            }
8291        }
8292
8293        class StreamFeederThread extends RestoreEngine implements Runnable {
8294            final String TAG = "StreamFeederThread";
8295            FullRestoreEngine mEngine;
8296
8297            // pipe through which we read data from the transport. [0] read, [1] write
8298            ParcelFileDescriptor[] mTransportPipes;
8299
8300            // pipe through which the engine will read data.  [0] read, [1] write
8301            ParcelFileDescriptor[] mEnginePipes;
8302
8303            public StreamFeederThread() throws IOException {
8304                mTransportPipes = ParcelFileDescriptor.createPipe();
8305                mEnginePipes = ParcelFileDescriptor.createPipe();
8306                setRunning(true);
8307            }
8308
8309            @Override
8310            public void run() {
8311                UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
8312                int status = BackupTransport.TRANSPORT_OK;
8313
8314                EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
8315                        mCurrentPackage.packageName);
8316
8317                mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false);
8318                EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]);
8319
8320                ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
8321                ParcelFileDescriptor tReadEnd = mTransportPipes[0];
8322                ParcelFileDescriptor tWriteEnd = mTransportPipes[1];
8323
8324                int bufferSize = 32 * 1024;
8325                byte[] buffer = new byte[bufferSize];
8326                FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor());
8327                FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor());
8328
8329                // spin up the engine and start moving data to it
8330                new Thread(eThread, "unified-restore-engine").start();
8331
8332                try {
8333                    while (status == BackupTransport.TRANSPORT_OK) {
8334                        // have the transport write some of the restoring data to us
8335                        int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd);
8336                        if (result > 0) {
8337                            // The transport wrote this many bytes of restore data to the
8338                            // pipe, so pass it along to the engine.
8339                            if (MORE_DEBUG) {
8340                                Slog.v(TAG, "  <- transport provided chunk size " + result);
8341                            }
8342                            if (result > bufferSize) {
8343                                bufferSize = result;
8344                                buffer = new byte[bufferSize];
8345                            }
8346                            int toCopy = result;
8347                            while (toCopy > 0) {
8348                                int n = transportIn.read(buffer, 0, toCopy);
8349                                engineOut.write(buffer, 0, n);
8350                                toCopy -= n;
8351                                if (MORE_DEBUG) {
8352                                    Slog.v(TAG, "  -> wrote " + n + " to engine, left=" + toCopy);
8353                                }
8354                            }
8355                        } else if (result == BackupTransport.NO_MORE_DATA) {
8356                            // Clean finish.  Wind up and we're done!
8357                            if (MORE_DEBUG) {
8358                                Slog.i(TAG, "Got clean full-restore EOF for "
8359                                        + mCurrentPackage.packageName);
8360                            }
8361                            status = BackupTransport.TRANSPORT_OK;
8362                            break;
8363                        } else {
8364                            // Transport reported some sort of failure; the fall-through
8365                            // handling will deal properly with that.
8366                            Slog.e(TAG, "Error " + result + " streaming restore for "
8367                                    + mCurrentPackage.packageName);
8368                            EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8369                            status = result;
8370                        }
8371                    }
8372                    if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through");
8373                } catch (IOException e) {
8374                    // We lost our ability to communicate via the pipes.  That's worrying
8375                    // but potentially recoverable; abandon this package's restore but
8376                    // carry on with the next restore target.
8377                    Slog.e(TAG, "Unable to route data for restore");
8378                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8379                            mCurrentPackage.packageName, "I/O error on pipes");
8380                    status = BackupTransport.AGENT_ERROR;
8381                } catch (RemoteException e) {
8382                    // The transport went away; terminate the whole operation.  Closing
8383                    // the sockets will wake up the engine and it will then tidy up the
8384                    // remote end.
8385                    Slog.e(TAG, "Transport failed during restore");
8386                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8387                    status = BackupTransport.TRANSPORT_ERROR;
8388                } finally {
8389                    // Close the transport pipes and *our* end of the engine pipe,
8390                    // but leave the engine thread's end open so that it properly
8391                    // hits EOF and winds up its operations.
8392                    IoUtils.closeQuietly(mEnginePipes[1]);
8393                    IoUtils.closeQuietly(mTransportPipes[0]);
8394                    IoUtils.closeQuietly(mTransportPipes[1]);
8395
8396                    // Don't proceed until the engine has finished
8397                    eThread.waitForResult();
8398
8399                    if (MORE_DEBUG) {
8400                        Slog.i(TAG, "engine thread finished; proceeding");
8401                    }
8402
8403                    // Now we're really done with this one too
8404                    IoUtils.closeQuietly(mEnginePipes[0]);
8405
8406                    // If we hit a transport-level error, we are done with everything;
8407                    // if we hit an agent error we just go back to running the queue.
8408                    if (status == BackupTransport.TRANSPORT_OK) {
8409                        // Clean finish means we issue the restore-finished callback
8410                        nextState = UnifiedRestoreState.RESTORE_FINISHED;
8411
8412                        // the engine bound the target's agent, so recover that binding
8413                        // to use for the callback.
8414                        mAgent = mEngine.getAgent();
8415                    } else {
8416                        // Something went wrong somewhere.  Whether it was at the transport
8417                        // level is immaterial; we need to tell the transport to bail
8418                        try {
8419                            mTransport.abortFullRestore();
8420                        } catch (RemoteException e) {
8421                            // transport itself is dead; make sure we handle this as a
8422                            // fatal error
8423                            status = BackupTransport.TRANSPORT_ERROR;
8424                        }
8425
8426                        // We also need to wipe the current target's data, as it's probably
8427                        // in an incoherent state.
8428                        clearApplicationDataSynchronous(mCurrentPackage.packageName);
8429
8430                        // Schedule the next state based on the nature of our failure
8431                        if (status == BackupTransport.TRANSPORT_ERROR) {
8432                            nextState = UnifiedRestoreState.FINAL;
8433                        } else {
8434                            nextState = UnifiedRestoreState.RUNNING_QUEUE;
8435                        }
8436                    }
8437                    executeNextState(nextState);
8438                    setRunning(false);
8439                }
8440            }
8441
8442        }
8443
8444        class EngineThread implements Runnable {
8445            FullRestoreEngine mEngine;
8446            FileInputStream mEngineStream;
8447
8448            EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
8449                mEngine = engine;
8450                engine.setRunning(true);
8451                mEngineStream = new FileInputStream(engineSocket.getFileDescriptor());
8452            }
8453
8454            public boolean isRunning() {
8455                return mEngine.isRunning();
8456            }
8457
8458            public int waitForResult() {
8459                return mEngine.waitForResult();
8460            }
8461
8462            @Override
8463            public void run() {
8464                while (mEngine.isRunning()) {
8465                    // Tell it to be sure to leave the agent instance up after finishing
8466                    mEngine.restoreOneFile(mEngineStream, false);
8467                }
8468            }
8469        }
8470
8471        // state FINAL : tear everything down and we're done.
8472        private void finalizeRestore() {
8473            if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
8474
8475            try {
8476                mTransport.finishRestore();
8477            } catch (Exception e) {
8478                Slog.e(TAG, "Error finishing restore", e);
8479            }
8480
8481            // Tell the observer we're done
8482            if (mObserver != null) {
8483                try {
8484                    mObserver.restoreFinished(mStatus);
8485                } catch (RemoteException e) {
8486                    Slog.d(TAG, "Restore observer died at restoreFinished");
8487                }
8488            }
8489
8490            // Clear any ongoing session timeout.
8491            mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
8492
8493            // If we have a PM token, we must under all circumstances be sure to
8494            // handshake when we've finished.
8495            if (mPmToken > 0) {
8496                if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
8497                try {
8498                    mPackageManagerBinder.finishPackageInstall(mPmToken);
8499                } catch (RemoteException e) { /* can't happen */ }
8500            } else {
8501                // We were invoked via an active restore session, not by the Package
8502                // Manager, so start up the session timeout again.
8503                mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
8504                        TIMEOUT_RESTORE_INTERVAL);
8505            }
8506
8507            // Kick off any work that may be needed regarding app widget restores
8508            // TODO: http://b/22388012
8509            AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM);
8510
8511            // If this was a full-system restore, record the ancestral
8512            // dataset information
8513            if (mIsSystemRestore && mPmAgent != null) {
8514                mAncestralPackages = mPmAgent.getRestoredPackages();
8515                mAncestralToken = mToken;
8516                writeRestoreTokens();
8517            }
8518
8519            // done; we can finally release the wakelock and be legitimately done.
8520            Slog.i(TAG, "Restore complete.");
8521            mWakelock.release();
8522        }
8523
8524        void keyValueAgentErrorCleanup() {
8525            // If the agent fails restore, it might have put the app's data
8526            // into an incoherent state.  For consistency we wipe its data
8527            // again in this case before continuing with normal teardown
8528            clearApplicationDataSynchronous(mCurrentPackage.packageName);
8529            keyValueAgentCleanup();
8530        }
8531
8532        // TODO: clean up naming; this is now used at finish by both k/v and stream restores
8533        void keyValueAgentCleanup() {
8534            mBackupDataName.delete();
8535            mStageName.delete();
8536            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
8537            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
8538            mBackupData = mNewState = null;
8539
8540            // if everything went okay, remember the recorded state now
8541            //
8542            // !!! TODO: the restored data could be migrated on the server
8543            // side into the current dataset.  In that case the new state file
8544            // we just created would reflect the data already extant in the
8545            // backend, so there'd be nothing more to do.  Until that happens,
8546            // however, we need to make sure that we record the data to the
8547            // current backend dataset.  (Yes, this means shipping the data over
8548            // the wire in both directions.  That's bad, but consistency comes
8549            // first, then efficiency.)  Once we introduce server-side data
8550            // migration to the newly-restored device's dataset, we will change
8551            // the following from a discard of the newly-written state to the
8552            // "correct" operation of renaming into the canonical state blob.
8553            mNewStateName.delete();                      // TODO: remove; see above comment
8554            //mNewStateName.renameTo(mSavedStateName);   // TODO: replace with this
8555
8556            // If this wasn't the PM pseudopackage, tear down the agent side
8557            if (mCurrentPackage.applicationInfo != null) {
8558                // unbind and tidy up even on timeout or failure
8559                try {
8560                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
8561
8562                    // The agent was probably running with a stub Application object,
8563                    // which isn't a valid run mode for the main app logic.  Shut
8564                    // down the app so that next time it's launched, it gets the
8565                    // usual full initialization.  Note that this is only done for
8566                    // full-system restores: when a single app has requested a restore,
8567                    // it is explicitly not killed following that operation.
8568                    //
8569                    // We execute this kill when these conditions hold:
8570                    //    1. it's not a system-uid process,
8571                    //    2. the app did not request its own restore (mTargetPackage == null), and either
8572                    //    3a. the app is a full-data target (TYPE_FULL_STREAM) or
8573                    //     b. the app does not state android:killAfterRestore="false" in its manifest
8574                    final int appFlags = mCurrentPackage.applicationInfo.flags;
8575                    final boolean killAfterRestore =
8576                            (mCurrentPackage.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
8577                            && ((mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM)
8578                                    || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0));
8579
8580                    if (mTargetPackage == null && killAfterRestore) {
8581                        if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
8582                                + mCurrentPackage.applicationInfo.processName);
8583                        mActivityManager.killApplicationProcess(
8584                                mCurrentPackage.applicationInfo.processName,
8585                                mCurrentPackage.applicationInfo.uid);
8586                    }
8587                } catch (RemoteException e) {
8588                    // can't happen; we run in the same process as the activity manager
8589                }
8590            }
8591
8592            // The caller is responsible for reestablishing the state machine; our
8593            // responsibility here is to clear the decks for whatever comes next.
8594            mBackupHandler.removeMessages(MSG_TIMEOUT, this);
8595            synchronized (mCurrentOpLock) {
8596                mCurrentOperations.clear();
8597            }
8598        }
8599
8600        @Override
8601        public void operationComplete(long unusedResult) {
8602            if (MORE_DEBUG) {
8603                Slog.i(TAG, "operationComplete() during restore: target="
8604                        + mCurrentPackage.packageName
8605                        + " state=" + mState);
8606            }
8607
8608            final UnifiedRestoreState nextState;
8609            switch (mState) {
8610                case INITIAL:
8611                    // We've just (manually) restored the PMBA.  It doesn't need the
8612                    // additional restore-finished callback so we bypass that and go
8613                    // directly to running the queue.
8614                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8615                    break;
8616
8617                case RESTORE_KEYVALUE:
8618                case RESTORE_FULL: {
8619                    // Okay, we've just heard back from the agent that it's done with
8620                    // the restore itself.  We now have to send the same agent its
8621                    // doRestoreFinished() callback, so roll into that state.
8622                    nextState = UnifiedRestoreState.RESTORE_FINISHED;
8623                    break;
8624                }
8625
8626                case RESTORE_FINISHED: {
8627                    // Okay, we're done with this package.  Tidy up and go on to the next
8628                    // app in the queue.
8629                    int size = (int) mBackupDataName.length();
8630                    EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
8631                            mCurrentPackage.packageName, size);
8632
8633                    // Just go back to running the restore queue
8634                    keyValueAgentCleanup();
8635
8636                    // If there was widget state associated with this app, get the OS to
8637                    // incorporate it into current bookeeping and then pass that along to
8638                    // the app as part of the restore-time work.
8639                    if (mWidgetData != null) {
8640                        restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
8641                    }
8642
8643                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8644                    break;
8645                }
8646
8647                default: {
8648                    // Some kind of horrible semantic error; we're in an unexpected state.
8649                    // Back off hard and wind up.
8650                    Slog.e(TAG, "Unexpected restore callback into state " + mState);
8651                    keyValueAgentErrorCleanup();
8652                    nextState = UnifiedRestoreState.FINAL;
8653                    break;
8654                }
8655            }
8656
8657            executeNextState(nextState);
8658        }
8659
8660        // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
8661        @Override
8662        public void handleTimeout() {
8663            Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
8664            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8665                    mCurrentPackage.packageName, "restore timeout");
8666            // Handle like an agent that threw on invocation: wipe it and go on to the next
8667            keyValueAgentErrorCleanup();
8668            executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8669        }
8670
8671        void executeNextState(UnifiedRestoreState nextState) {
8672            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
8673                    + this + " nextState=" + nextState);
8674            mState = nextState;
8675            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
8676            mBackupHandler.sendMessage(msg);
8677        }
8678
8679        // restore observer support
8680        void sendStartRestore(int numPackages) {
8681            if (mObserver != null) {
8682                try {
8683                    mObserver.restoreStarting(numPackages);
8684                } catch (RemoteException e) {
8685                    Slog.w(TAG, "Restore observer went away: startRestore");
8686                    mObserver = null;
8687                }
8688            }
8689        }
8690
8691        void sendOnRestorePackage(String name) {
8692            if (mObserver != null) {
8693                if (mObserver != null) {
8694                    try {
8695                        mObserver.onUpdate(mCount, name);
8696                    } catch (RemoteException e) {
8697                        Slog.d(TAG, "Restore observer died in onUpdate");
8698                        mObserver = null;
8699                    }
8700                }
8701            }
8702        }
8703
8704        void sendEndRestore() {
8705            if (mObserver != null) {
8706                try {
8707                    mObserver.restoreFinished(mStatus);
8708                } catch (RemoteException e) {
8709                    Slog.w(TAG, "Restore observer went away: endRestore");
8710                    mObserver = null;
8711                }
8712            }
8713        }
8714    }
8715
8716    class PerformClearTask implements Runnable {
8717        IBackupTransport mTransport;
8718        PackageInfo mPackage;
8719
8720        PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
8721            mTransport = transport;
8722            mPackage = packageInfo;
8723        }
8724
8725        public void run() {
8726            try {
8727                // Clear the on-device backup state to ensure a full backup next time
8728                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
8729                File stateFile = new File(stateDir, mPackage.packageName);
8730                stateFile.delete();
8731
8732                // Tell the transport to remove all the persistent storage for the app
8733                // TODO - need to handle failures
8734                mTransport.clearBackupData(mPackage);
8735            } catch (RemoteException e) {
8736                // can't happen; the transport is local
8737            } catch (Exception e) {
8738                Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
8739            } finally {
8740                try {
8741                    // TODO - need to handle failures
8742                    mTransport.finishBackup();
8743                } catch (RemoteException e) {
8744                    // can't happen; the transport is local
8745                }
8746
8747                // Last but not least, release the cpu
8748                mWakelock.release();
8749            }
8750        }
8751    }
8752
8753    class PerformInitializeTask implements Runnable {
8754        HashSet<String> mQueue;
8755
8756        PerformInitializeTask(HashSet<String> transportNames) {
8757            mQueue = transportNames;
8758        }
8759
8760        public void run() {
8761            try {
8762                for (String transportName : mQueue) {
8763                    IBackupTransport transport = getTransport(transportName);
8764                    if (transport == null) {
8765                        Slog.e(TAG, "Requested init for " + transportName + " but not found");
8766                        continue;
8767                    }
8768
8769                    Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
8770                    EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
8771                    long startRealtime = SystemClock.elapsedRealtime();
8772                    int status = transport.initializeDevice();
8773
8774                    if (status == BackupTransport.TRANSPORT_OK) {
8775                        status = transport.finishBackup();
8776                    }
8777
8778                    // Okay, the wipe really happened.  Clean up our local bookkeeping.
8779                    if (status == BackupTransport.TRANSPORT_OK) {
8780                        Slog.i(TAG, "Device init successful");
8781                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
8782                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
8783                        resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
8784                        EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
8785                        synchronized (mQueueLock) {
8786                            recordInitPendingLocked(false, transportName);
8787                        }
8788                    } else {
8789                        // If this didn't work, requeue this one and try again
8790                        // after a suitable interval
8791                        Slog.e(TAG, "Transport error in initializeDevice()");
8792                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
8793                        synchronized (mQueueLock) {
8794                            recordInitPendingLocked(true, transportName);
8795                        }
8796                        // do this via another alarm to make sure of the wakelock states
8797                        long delay = transport.requestBackupTime();
8798                        Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay);
8799                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
8800                                System.currentTimeMillis() + delay, mRunInitIntent);
8801                    }
8802                }
8803            } catch (RemoteException e) {
8804                // can't happen; the transports are local
8805            } catch (Exception e) {
8806                Slog.e(TAG, "Unexpected error performing init", e);
8807            } finally {
8808                // Done; release the wakelock
8809                mWakelock.release();
8810            }
8811        }
8812    }
8813
8814    private void dataChangedImpl(String packageName) {
8815        HashSet<String> targets = dataChangedTargets(packageName);
8816        dataChangedImpl(packageName, targets);
8817    }
8818
8819    private void dataChangedImpl(String packageName, HashSet<String> targets) {
8820        // Record that we need a backup pass for the caller.  Since multiple callers
8821        // may share a uid, we need to note all candidates within that uid and schedule
8822        // a backup pass for each of them.
8823        if (targets == null) {
8824            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8825                   + " uid=" + Binder.getCallingUid());
8826            return;
8827        }
8828
8829        synchronized (mQueueLock) {
8830            // Note that this client has made data changes that need to be backed up
8831            if (targets.contains(packageName)) {
8832                // Add the caller to the set of pending backups.  If there is
8833                // one already there, then overwrite it, but no harm done.
8834                BackupRequest req = new BackupRequest(packageName);
8835                if (mPendingBackups.put(packageName, req) == null) {
8836                    if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
8837
8838                    // Journal this request in case of crash.  The put()
8839                    // operation returned null when this package was not already
8840                    // in the set; we want to avoid touching the disk redundantly.
8841                    writeToJournalLocked(packageName);
8842                }
8843            }
8844        }
8845
8846        // ...and schedule a backup pass if necessary
8847        KeyValueBackupJob.schedule(mContext);
8848    }
8849
8850    // Note: packageName is currently unused, but may be in the future
8851    private HashSet<String> dataChangedTargets(String packageName) {
8852        // If the caller does not hold the BACKUP permission, it can only request a
8853        // backup of its own data.
8854        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8855                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8856            synchronized (mBackupParticipants) {
8857                return mBackupParticipants.get(Binder.getCallingUid());
8858            }
8859        }
8860
8861        // a caller with full permission can ask to back up any participating app
8862        HashSet<String> targets = new HashSet<String>();
8863        if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
8864            targets.add(PACKAGE_MANAGER_SENTINEL);
8865        } else {
8866            synchronized (mBackupParticipants) {
8867                int N = mBackupParticipants.size();
8868                for (int i = 0; i < N; i++) {
8869                    HashSet<String> s = mBackupParticipants.valueAt(i);
8870                    if (s != null) {
8871                        targets.addAll(s);
8872                    }
8873                }
8874            }
8875        }
8876        return targets;
8877    }
8878
8879    private void writeToJournalLocked(String str) {
8880        RandomAccessFile out = null;
8881        try {
8882            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
8883            out = new RandomAccessFile(mJournal, "rws");
8884            out.seek(out.length());
8885            out.writeUTF(str);
8886        } catch (IOException e) {
8887            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
8888            mJournal = null;
8889        } finally {
8890            try { if (out != null) out.close(); } catch (IOException e) {}
8891        }
8892    }
8893
8894    // ----- IBackupManager binder interface -----
8895
8896    public void dataChanged(final String packageName) {
8897        final int callingUserHandle = UserHandle.getCallingUserId();
8898        if (callingUserHandle != UserHandle.USER_SYSTEM) {
8899            // TODO: http://b/22388012
8900            // App is running under a non-owner user profile.  For now, we do not back
8901            // up data from secondary user profiles.
8902            // TODO: backups for all user profiles although don't add backup for profiles
8903            // without adding admin control in DevicePolicyManager.
8904            if (MORE_DEBUG) {
8905                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
8906                        + callingUserHandle);
8907            }
8908            return;
8909        }
8910
8911        final HashSet<String> targets = dataChangedTargets(packageName);
8912        if (targets == null) {
8913            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8914                   + " uid=" + Binder.getCallingUid());
8915            return;
8916        }
8917
8918        mBackupHandler.post(new Runnable() {
8919                public void run() {
8920                    dataChangedImpl(packageName, targets);
8921                }
8922            });
8923    }
8924
8925    // Clear the given package's backup data from the current transport
8926    public void clearBackupData(String transportName, String packageName) {
8927        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
8928        PackageInfo info;
8929        try {
8930            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
8931        } catch (NameNotFoundException e) {
8932            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
8933            return;
8934        }
8935
8936        // If the caller does not hold the BACKUP permission, it can only request a
8937        // wipe of its own backed-up data.
8938        HashSet<String> apps;
8939        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8940                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8941            apps = mBackupParticipants.get(Binder.getCallingUid());
8942        } else {
8943            // a caller with full permission can ask to back up any participating app
8944            // !!! TODO: allow data-clear of ANY app?
8945            if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
8946            apps = new HashSet<String>();
8947            int N = mBackupParticipants.size();
8948            for (int i = 0; i < N; i++) {
8949                HashSet<String> s = mBackupParticipants.valueAt(i);
8950                if (s != null) {
8951                    apps.addAll(s);
8952                }
8953            }
8954        }
8955
8956        // Is the given app an available participant?
8957        if (apps.contains(packageName)) {
8958            // found it; fire off the clear request
8959            if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
8960            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
8961            synchronized (mQueueLock) {
8962                final IBackupTransport transport = getTransport(transportName);
8963                if (transport == null) {
8964                    // transport is currently unavailable -- make sure to retry
8965                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
8966                            new ClearRetryParams(transportName, packageName));
8967                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
8968                    return;
8969                }
8970                long oldId = Binder.clearCallingIdentity();
8971                mWakelock.acquire();
8972                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
8973                        new ClearParams(transport, info));
8974                mBackupHandler.sendMessage(msg);
8975                Binder.restoreCallingIdentity(oldId);
8976            }
8977        }
8978    }
8979
8980    // Run a backup pass immediately for any applications that have declared
8981    // that they have pending updates.
8982    public void backupNow() {
8983        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
8984
8985        if (mPowerManager.isPowerSaveMode()) {
8986            if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
8987            KeyValueBackupJob.schedule(mContext);   // try again in several hours
8988        } else {
8989            if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
8990            synchronized (mQueueLock) {
8991                // Fire the intent that kicks off the whole shebang...
8992                try {
8993                    mRunBackupIntent.send();
8994                } catch (PendingIntent.CanceledException e) {
8995                    // should never happen
8996                    Slog.e(TAG, "run-backup intent cancelled!");
8997                }
8998
8999                // ...and cancel any pending scheduled job, because we've just superseded it
9000                KeyValueBackupJob.cancel(mContext);
9001            }
9002        }
9003    }
9004
9005    boolean deviceIsProvisioned() {
9006        final ContentResolver resolver = mContext.getContentResolver();
9007        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
9008    }
9009
9010    // Run a *full* backup pass for the given packages, writing the resulting data stream
9011    // to the supplied file descriptor.  This method is synchronous and does not return
9012    // to the caller until the backup has been completed.
9013    //
9014    // This is the variant used by 'adb backup'; it requires on-screen confirmation
9015    // by the user because it can be used to offload data over untrusted USB.
9016    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
9017            boolean includeObbs, boolean includeShared, boolean doWidgets,
9018            boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) {
9019        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
9020
9021        final int callingUserHandle = UserHandle.getCallingUserId();
9022        // TODO: http://b/22388012
9023        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9024            throw new IllegalStateException("Backup supported only for the device owner");
9025        }
9026
9027        // Validate
9028        if (!doAllApps) {
9029            if (!includeShared) {
9030                // If we're backing up shared data (sdcard or equivalent), then we can run
9031                // without any supplied app names.  Otherwise, we'd be doing no work, so
9032                // report the error.
9033                if (pkgList == null || pkgList.length == 0) {
9034                    throw new IllegalArgumentException(
9035                            "Backup requested but neither shared nor any apps named");
9036                }
9037            }
9038        }
9039
9040        long oldId = Binder.clearCallingIdentity();
9041        try {
9042            // Doesn't make sense to do a full backup prior to setup
9043            if (!deviceIsProvisioned()) {
9044                Slog.i(TAG, "Full backup not supported before setup");
9045                return;
9046            }
9047
9048            if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
9049                    + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
9050                    + " system=" + includeSystem + " pkgs=" + pkgList);
9051            Slog.i(TAG, "Beginning full backup...");
9052
9053            FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
9054                    includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList);
9055            final int token = generateToken();
9056            synchronized (mFullConfirmations) {
9057                mFullConfirmations.put(token, params);
9058            }
9059
9060            // start up the confirmation UI
9061            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
9062            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
9063                Slog.e(TAG, "Unable to launch full backup confirmation");
9064                mFullConfirmations.delete(token);
9065                return;
9066            }
9067
9068            // make sure the screen is lit for the user interaction
9069            mPowerManager.userActivity(SystemClock.uptimeMillis(),
9070                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
9071                    0);
9072
9073            // start the confirmation countdown
9074            startConfirmationTimeout(token, params);
9075
9076            // wait for the backup to be performed
9077            if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
9078            waitForCompletion(params);
9079        } finally {
9080            try {
9081                fd.close();
9082            } catch (IOException e) {
9083                // just eat it
9084            }
9085            Binder.restoreCallingIdentity(oldId);
9086            Slog.d(TAG, "Full backup processing complete.");
9087        }
9088    }
9089
9090    public void fullTransportBackup(String[] pkgNames) {
9091        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
9092                "fullTransportBackup");
9093
9094        final int callingUserHandle = UserHandle.getCallingUserId();
9095        // TODO: http://b/22388012
9096        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9097            throw new IllegalStateException("Restore supported only for the device owner");
9098        }
9099
9100        if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
9101            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
9102        } else {
9103            if (DEBUG) {
9104                Slog.d(TAG, "fullTransportBackup()");
9105            }
9106
9107            final long oldId = Binder.clearCallingIdentity();
9108            try {
9109                CountDownLatch latch = new CountDownLatch(1);
9110                PerformFullTransportBackupTask task = new PerformFullTransportBackupTask(null,
9111                        pkgNames, false, null, latch, null, false /* userInitiated */);
9112                // Acquiring wakelock for PerformFullTransportBackupTask before its start.
9113                mWakelock.acquire();
9114                (new Thread(task, "full-transport-master")).start();
9115                do {
9116                    try {
9117                        latch.await();
9118                        break;
9119                    } catch (InterruptedException e) {
9120                        // Just go back to waiting for the latch to indicate completion
9121                    }
9122                } while (true);
9123
9124                // We just ran a backup on these packages, so kick them to the end of the queue
9125                final long now = System.currentTimeMillis();
9126                for (String pkg : pkgNames) {
9127                    enqueueFullBackup(pkg, now);
9128                }
9129            } finally {
9130                Binder.restoreCallingIdentity(oldId);
9131            }
9132        }
9133
9134        if (DEBUG) {
9135            Slog.d(TAG, "Done with full transport backup.");
9136        }
9137    }
9138
9139    public void fullRestore(ParcelFileDescriptor fd) {
9140        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
9141
9142        final int callingUserHandle = UserHandle.getCallingUserId();
9143        // TODO: http://b/22388012
9144        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9145            throw new IllegalStateException("Restore supported only for the device owner");
9146        }
9147
9148        long oldId = Binder.clearCallingIdentity();
9149
9150        try {
9151            // Check whether the device has been provisioned -- we don't handle
9152            // full restores prior to completing the setup process.
9153            if (!deviceIsProvisioned()) {
9154                Slog.i(TAG, "Full restore not permitted before setup");
9155                return;
9156            }
9157
9158            Slog.i(TAG, "Beginning full restore...");
9159
9160            FullRestoreParams params = new FullRestoreParams(fd);
9161            final int token = generateToken();
9162            synchronized (mFullConfirmations) {
9163                mFullConfirmations.put(token, params);
9164            }
9165
9166            // start up the confirmation UI
9167            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
9168            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
9169                Slog.e(TAG, "Unable to launch full restore confirmation");
9170                mFullConfirmations.delete(token);
9171                return;
9172            }
9173
9174            // make sure the screen is lit for the user interaction
9175            mPowerManager.userActivity(SystemClock.uptimeMillis(),
9176                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
9177                    0);
9178
9179            // start the confirmation countdown
9180            startConfirmationTimeout(token, params);
9181
9182            // wait for the restore to be performed
9183            if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
9184            waitForCompletion(params);
9185        } finally {
9186            try {
9187                fd.close();
9188            } catch (IOException e) {
9189                Slog.w(TAG, "Error trying to close fd after full restore: " + e);
9190            }
9191            Binder.restoreCallingIdentity(oldId);
9192            Slog.i(TAG, "Full restore processing complete.");
9193        }
9194    }
9195
9196    boolean startConfirmationUi(int token, String action) {
9197        try {
9198            Intent confIntent = new Intent(action);
9199            confIntent.setClassName("com.android.backupconfirm",
9200                    "com.android.backupconfirm.BackupRestoreConfirmation");
9201            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
9202            confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
9203            mContext.startActivity(confIntent);
9204        } catch (ActivityNotFoundException e) {
9205            return false;
9206        }
9207        return true;
9208    }
9209
9210    void startConfirmationTimeout(int token, FullParams params) {
9211        if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
9212                + TIMEOUT_FULL_CONFIRMATION + " millis");
9213        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
9214                token, 0, params);
9215        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
9216    }
9217
9218    void waitForCompletion(FullParams params) {
9219        synchronized (params.latch) {
9220            while (params.latch.get() == false) {
9221                try {
9222                    params.latch.wait();
9223                } catch (InterruptedException e) { /* never interrupted */ }
9224            }
9225        }
9226    }
9227
9228    void signalFullBackupRestoreCompletion(FullParams params) {
9229        synchronized (params.latch) {
9230            params.latch.set(true);
9231            params.latch.notifyAll();
9232        }
9233    }
9234
9235    // Confirm that the previously-requested full backup/restore operation can proceed.  This
9236    // is used to require a user-facing disclosure about the operation.
9237    public void acknowledgeFullBackupOrRestore(int token, boolean allow,
9238            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
9239        if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
9240                + " allow=" + allow);
9241
9242        // TODO: possibly require not just this signature-only permission, but even
9243        // require that the specific designated confirmation-UI app uid is the caller?
9244        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
9245
9246        long oldId = Binder.clearCallingIdentity();
9247        try {
9248
9249            FullParams params;
9250            synchronized (mFullConfirmations) {
9251                params = mFullConfirmations.get(token);
9252                if (params != null) {
9253                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
9254                    mFullConfirmations.delete(token);
9255
9256                    if (allow) {
9257                        final int verb = params instanceof FullBackupParams
9258                                ? MSG_RUN_ADB_BACKUP
9259                                : MSG_RUN_ADB_RESTORE;
9260
9261                        params.observer = observer;
9262                        params.curPassword = curPassword;
9263
9264                        params.encryptPassword = encPpassword;
9265
9266                        if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
9267                        mWakelock.acquire();
9268                        Message msg = mBackupHandler.obtainMessage(verb, params);
9269                        mBackupHandler.sendMessage(msg);
9270                    } else {
9271                        Slog.w(TAG, "User rejected full backup/restore operation");
9272                        // indicate completion without having actually transferred any data
9273                        signalFullBackupRestoreCompletion(params);
9274                    }
9275                } else {
9276                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
9277                }
9278            }
9279        } finally {
9280            Binder.restoreCallingIdentity(oldId);
9281        }
9282    }
9283
9284    private static boolean backupSettingMigrated(int userId) {
9285        File base = new File(Environment.getDataDirectory(), "backup");
9286        File enableFile = new File(base, BACKUP_ENABLE_FILE);
9287        return enableFile.exists();
9288    }
9289
9290    private static boolean readBackupEnableState(int userId) {
9291        File base = new File(Environment.getDataDirectory(), "backup");
9292        File enableFile = new File(base, BACKUP_ENABLE_FILE);
9293        if (enableFile.exists()) {
9294            try (FileInputStream fin = new FileInputStream(enableFile)) {
9295                int state = fin.read();
9296                return state != 0;
9297            } catch (IOException e) {
9298                // can't read the file; fall through to assume disabled
9299                Slog.e(TAG, "Cannot read enable state; assuming disabled");
9300            }
9301        } else {
9302            if (DEBUG) {
9303                Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
9304            }
9305        }
9306        return false;
9307    }
9308
9309    private static void writeBackupEnableState(boolean enable, int userId) {
9310        File base = new File(Environment.getDataDirectory(), "backup");
9311        File enableFile = new File(base, BACKUP_ENABLE_FILE);
9312        File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
9313        FileOutputStream fout = null;
9314        try {
9315            fout = new FileOutputStream(stage);
9316            fout.write(enable ? 1 : 0);
9317            fout.close();
9318            stage.renameTo(enableFile);
9319            // will be synced immediately by the try-with-resources call to close()
9320        } catch (IOException|RuntimeException e) {
9321            // Whoops; looks like we're doomed.  Roll everything out, disabled,
9322            // including the legacy state.
9323            Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
9324                    + e.getMessage());
9325
9326            final ContentResolver r = sInstance.mContext.getContentResolver();
9327            Settings.Secure.putStringForUser(r,
9328                    Settings.Secure.BACKUP_ENABLED, null, userId);
9329            enableFile.delete();
9330            stage.delete();
9331        } finally {
9332            IoUtils.closeQuietly(fout);
9333        }
9334    }
9335
9336    // Enable/disable backups
9337    public void setBackupEnabled(boolean enable) {
9338        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9339                "setBackupEnabled");
9340
9341        Slog.i(TAG, "Backup enabled => " + enable);
9342
9343        long oldId = Binder.clearCallingIdentity();
9344        try {
9345            boolean wasEnabled = mEnabled;
9346            synchronized (this) {
9347                writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
9348                mEnabled = enable;
9349            }
9350
9351            synchronized (mQueueLock) {
9352                if (enable && !wasEnabled && mProvisioned) {
9353                    // if we've just been enabled, start scheduling backup passes
9354                    KeyValueBackupJob.schedule(mContext);
9355                    scheduleNextFullBackupJob(0);
9356                } else if (!enable) {
9357                    // No longer enabled, so stop running backups
9358                    if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
9359
9360                    KeyValueBackupJob.cancel(mContext);
9361
9362                    // This also constitutes an opt-out, so we wipe any data for
9363                    // this device from the backend.  We start that process with
9364                    // an alarm in order to guarantee wakelock states.
9365                    if (wasEnabled && mProvisioned) {
9366                        // NOTE: we currently flush every registered transport, not just
9367                        // the currently-active one.
9368                        HashSet<String> allTransports;
9369                        synchronized (mTransports) {
9370                            allTransports = new HashSet<String>(mTransports.keySet());
9371                        }
9372                        // build the set of transports for which we are posting an init
9373                        for (String transport : allTransports) {
9374                            recordInitPendingLocked(true, transport);
9375                        }
9376                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
9377                                mRunInitIntent);
9378                    }
9379                }
9380            }
9381        } finally {
9382            Binder.restoreCallingIdentity(oldId);
9383        }
9384    }
9385
9386    // Enable/disable automatic restore of app data at install time
9387    public void setAutoRestore(boolean doAutoRestore) {
9388        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9389                "setAutoRestore");
9390
9391        Slog.i(TAG, "Auto restore => " + doAutoRestore);
9392
9393        final long oldId = Binder.clearCallingIdentity();
9394        try {
9395            synchronized (this) {
9396                Settings.Secure.putInt(mContext.getContentResolver(),
9397                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
9398                mAutoRestore = doAutoRestore;
9399            }
9400        } finally {
9401            Binder.restoreCallingIdentity(oldId);
9402        }
9403    }
9404
9405    // Mark the backup service as having been provisioned
9406    public void setBackupProvisioned(boolean available) {
9407        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9408                "setBackupProvisioned");
9409        /*
9410         * This is now a no-op; provisioning is simply the device's own setup state.
9411         */
9412    }
9413
9414    // Report whether the backup mechanism is currently enabled
9415    public boolean isBackupEnabled() {
9416        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
9417        return mEnabled;    // no need to synchronize just to read it
9418    }
9419
9420    // Report the name of the currently active transport
9421    public String getCurrentTransport() {
9422        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9423                "getCurrentTransport");
9424        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
9425        return mCurrentTransport;
9426    }
9427
9428    // Report all known, available backup transports
9429    public String[] listAllTransports() {
9430        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
9431
9432        String[] list = null;
9433        ArrayList<String> known = new ArrayList<String>();
9434        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
9435            if (entry.getValue() != null) {
9436                known.add(entry.getKey());
9437            }
9438        }
9439
9440        if (known.size() > 0) {
9441            list = new String[known.size()];
9442            known.toArray(list);
9443        }
9444        return list;
9445    }
9446
9447    // Select which transport to use for the next backup operation.
9448    public String selectBackupTransport(String transport) {
9449        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9450                "selectBackupTransport");
9451
9452        synchronized (mTransports) {
9453            final long oldId = Binder.clearCallingIdentity();
9454            try {
9455                String prevTransport = mCurrentTransport;
9456                mCurrentTransport = transport;
9457                Settings.Secure.putString(mContext.getContentResolver(),
9458                        Settings.Secure.BACKUP_TRANSPORT, transport);
9459                Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
9460                        + " returning " + prevTransport);
9461                return prevTransport;
9462            } finally {
9463                Binder.restoreCallingIdentity(oldId);
9464            }
9465        }
9466    }
9467
9468    // Supply the configuration Intent for the given transport.  If the name is not one
9469    // of the available transports, or if the transport does not supply any configuration
9470    // UI, the method returns null.
9471    public Intent getConfigurationIntent(String transportName) {
9472        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9473                "getConfigurationIntent");
9474
9475        synchronized (mTransports) {
9476            final IBackupTransport transport = mTransports.get(transportName);
9477            if (transport != null) {
9478                try {
9479                    final Intent intent = transport.configurationIntent();
9480                    if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
9481                            + intent);
9482                    return intent;
9483                } catch (RemoteException e) {
9484                    /* fall through to return null */
9485                }
9486            }
9487        }
9488
9489        return null;
9490    }
9491
9492    // Supply the configuration summary string for the given transport.  If the name is
9493    // not one of the available transports, or if the transport does not supply any
9494    // summary / destination string, the method can return null.
9495    //
9496    // This string is used VERBATIM as the summary text of the relevant Settings item!
9497    public String getDestinationString(String transportName) {
9498        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9499                "getDestinationString");
9500
9501        synchronized (mTransports) {
9502            final IBackupTransport transport = mTransports.get(transportName);
9503            if (transport != null) {
9504                try {
9505                    final String text = transport.currentDestinationString();
9506                    if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
9507                    return text;
9508                } catch (RemoteException e) {
9509                    /* fall through to return null */
9510                }
9511            }
9512        }
9513
9514        return null;
9515    }
9516
9517    // Supply the manage-data intent for the given transport.
9518    public Intent getDataManagementIntent(String transportName) {
9519        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9520                "getDataManagementIntent");
9521
9522        synchronized (mTransports) {
9523            final IBackupTransport transport = mTransports.get(transportName);
9524            if (transport != null) {
9525                try {
9526                    final Intent intent = transport.dataManagementIntent();
9527                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
9528                            + intent);
9529                    return intent;
9530                } catch (RemoteException e) {
9531                    /* fall through to return null */
9532                }
9533            }
9534        }
9535
9536        return null;
9537    }
9538
9539    // Supply the menu label for affordances that fire the manage-data intent
9540    // for the given transport.
9541    public String getDataManagementLabel(String transportName) {
9542        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9543                "getDataManagementLabel");
9544
9545        synchronized (mTransports) {
9546            final IBackupTransport transport = mTransports.get(transportName);
9547            if (transport != null) {
9548                try {
9549                    final String text = transport.dataManagementLabel();
9550                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
9551                    return text;
9552                } catch (RemoteException e) {
9553                    /* fall through to return null */
9554                }
9555            }
9556        }
9557
9558        return null;
9559    }
9560
9561    // Callback: a requested backup agent has been instantiated.  This should only
9562    // be called from the Activity Manager.
9563    public void agentConnected(String packageName, IBinder agentBinder) {
9564        synchronized(mAgentConnectLock) {
9565            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
9566                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
9567                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
9568                mConnectedAgent = agent;
9569                mConnecting = false;
9570            } else {
9571                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9572                        + " claiming agent connected");
9573            }
9574            mAgentConnectLock.notifyAll();
9575        }
9576    }
9577
9578    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
9579    // If the agent failed to come up in the first place, the agentBinder argument
9580    // will be null.  This should only be called from the Activity Manager.
9581    public void agentDisconnected(String packageName) {
9582        // TODO: handle backup being interrupted
9583        synchronized(mAgentConnectLock) {
9584            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
9585                mConnectedAgent = null;
9586                mConnecting = false;
9587            } else {
9588                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9589                        + " claiming agent disconnected");
9590            }
9591            mAgentConnectLock.notifyAll();
9592        }
9593    }
9594
9595    // An application being installed will need a restore pass, then the Package Manager
9596    // will need to be told when the restore is finished.
9597    public void restoreAtInstall(String packageName, int token) {
9598        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
9599            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9600                    + " attemping install-time restore");
9601            return;
9602        }
9603
9604        boolean skip = false;
9605
9606        long restoreSet = getAvailableRestoreToken(packageName);
9607        if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
9608                + " token=" + Integer.toHexString(token)
9609                + " restoreSet=" + Long.toHexString(restoreSet));
9610        if (restoreSet == 0) {
9611            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
9612            skip = true;
9613        }
9614
9615        // Do we have a transport to fetch data for us?
9616        IBackupTransport transport = getTransport(mCurrentTransport);
9617        if (transport == null) {
9618            if (DEBUG) Slog.w(TAG, "No transport");
9619            skip = true;
9620        }
9621
9622        if (!mAutoRestore) {
9623            if (DEBUG) {
9624                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
9625            }
9626            skip = true;
9627        }
9628
9629        if (!skip) {
9630            try {
9631                // okay, we're going to attempt a restore of this package from this restore set.
9632                // The eventual message back into the Package Manager to run the post-install
9633                // steps for 'token' will be issued from the restore handling code.
9634
9635                // This can throw and so *must* happen before the wakelock is acquired
9636                String dirName = transport.transportDirName();
9637
9638                mWakelock.acquire();
9639                if (MORE_DEBUG) {
9640                    Slog.d(TAG, "Restore at install of " + packageName);
9641                }
9642                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9643                msg.obj = new RestoreParams(transport, dirName, null,
9644                        restoreSet, packageName, token);
9645                mBackupHandler.sendMessage(msg);
9646            } catch (RemoteException e) {
9647                // Binding to the transport broke; back off and proceed with the installation.
9648                Slog.e(TAG, "Unable to contact transport");
9649                skip = true;
9650            }
9651        }
9652
9653        if (skip) {
9654            // Auto-restore disabled or no way to attempt a restore; just tell the Package
9655            // Manager to proceed with the post-install handling for this package.
9656            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
9657            try {
9658                mPackageManagerBinder.finishPackageInstall(token);
9659            } catch (RemoteException e) { /* can't happen */ }
9660        }
9661    }
9662
9663    // Hand off a restore session
9664    public IRestoreSession beginRestoreSession(String packageName, String transport) {
9665        if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
9666                + " transport=" + transport);
9667
9668        boolean needPermission = true;
9669        if (transport == null) {
9670            transport = mCurrentTransport;
9671
9672            if (packageName != null) {
9673                PackageInfo app = null;
9674                try {
9675                    app = mPackageManager.getPackageInfo(packageName, 0);
9676                } catch (NameNotFoundException nnf) {
9677                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9678                    throw new IllegalArgumentException("Package " + packageName + " not found");
9679                }
9680
9681                if (app.applicationInfo.uid == Binder.getCallingUid()) {
9682                    // So: using the current active transport, and the caller has asked
9683                    // that its own package will be restored.  In this narrow use case
9684                    // we do not require the caller to hold the permission.
9685                    needPermission = false;
9686                }
9687            }
9688        }
9689
9690        if (needPermission) {
9691            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9692                    "beginRestoreSession");
9693        } else {
9694            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
9695        }
9696
9697        synchronized(this) {
9698            if (mActiveRestoreSession != null) {
9699                Slog.d(TAG, "Restore session requested but one already active");
9700                return null;
9701            }
9702            mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
9703            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
9704        }
9705        return mActiveRestoreSession;
9706    }
9707
9708    void clearRestoreSession(ActiveRestoreSession currentSession) {
9709        synchronized(this) {
9710            if (currentSession != mActiveRestoreSession) {
9711                Slog.e(TAG, "ending non-current restore session");
9712            } else {
9713                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
9714                mActiveRestoreSession = null;
9715                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9716            }
9717        }
9718    }
9719
9720    // Note that a currently-active backup agent has notified us that it has
9721    // completed the given outstanding asynchronous backup/restore operation.
9722    public void opComplete(int token, long result) {
9723        if (MORE_DEBUG) {
9724            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
9725        }
9726        Operation op = null;
9727        synchronized (mCurrentOpLock) {
9728            op = mCurrentOperations.get(token);
9729            if (op != null) {
9730                op.state = OP_ACKNOWLEDGED;
9731            }
9732            mCurrentOpLock.notifyAll();
9733        }
9734
9735        // The completion callback, if any, is invoked on the handler
9736        if (op != null && op.callback != null) {
9737            Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
9738            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
9739            mBackupHandler.sendMessage(msg);
9740        }
9741    }
9742
9743    public boolean isAppEligibleForBackup(String packageName) {
9744        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9745                "isAppEligibleForBackup");
9746        try {
9747            PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
9748                    PackageManager.GET_SIGNATURES);
9749            if (!appIsEligibleForBackup(packageInfo.applicationInfo) ||
9750                    appIsStopped(packageInfo.applicationInfo)) {
9751                return false;
9752            }
9753            IBackupTransport transport = getTransport(mCurrentTransport);
9754            if (transport != null) {
9755                try {
9756                    return transport.isAppEligibleForBackup(packageInfo,
9757                        appGetsFullBackup(packageInfo));
9758                } catch (RemoteException e) {
9759                    Slog.e(TAG, "Unable to contact transport");
9760                }
9761            }
9762            // If transport is not present we couldn't tell that the package is not eligible.
9763            return true;
9764        } catch (NameNotFoundException e) {
9765            return false;
9766        }
9767    }
9768
9769    // ----- Restore session -----
9770
9771    class ActiveRestoreSession extends IRestoreSession.Stub {
9772        private static final String TAG = "RestoreSession";
9773
9774        private String mPackageName;
9775        private IBackupTransport mRestoreTransport = null;
9776        RestoreSet[] mRestoreSets = null;
9777        boolean mEnded = false;
9778        boolean mTimedOut = false;
9779
9780        ActiveRestoreSession(String packageName, String transport) {
9781            mPackageName = packageName;
9782            mRestoreTransport = getTransport(transport);
9783        }
9784
9785        public void markTimedOut() {
9786            mTimedOut = true;
9787        }
9788
9789        // --- Binder interface ---
9790        public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
9791            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9792                    "getAvailableRestoreSets");
9793            if (observer == null) {
9794                throw new IllegalArgumentException("Observer must not be null");
9795            }
9796
9797            if (mEnded) {
9798                throw new IllegalStateException("Restore session already ended");
9799            }
9800
9801            if (mTimedOut) {
9802                Slog.i(TAG, "Session already timed out");
9803                return -1;
9804            }
9805
9806            long oldId = Binder.clearCallingIdentity();
9807            try {
9808                if (mRestoreTransport == null) {
9809                    Slog.w(TAG, "Null transport getting restore sets");
9810                    return -1;
9811                }
9812
9813                // We know we're doing legit work now, so halt the timeout
9814                // until we're done.  It gets started again when the result
9815                // comes in.
9816                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9817
9818                // spin off the transport request to our service thread
9819                mWakelock.acquire();
9820                Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
9821                        new RestoreGetSetsParams(mRestoreTransport, this, observer));
9822                mBackupHandler.sendMessage(msg);
9823                return 0;
9824            } catch (Exception e) {
9825                Slog.e(TAG, "Error in getAvailableRestoreSets", e);
9826                return -1;
9827            } finally {
9828                Binder.restoreCallingIdentity(oldId);
9829            }
9830        }
9831
9832        public synchronized int restoreAll(long token, IRestoreObserver observer) {
9833            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9834                    "performRestore");
9835
9836            if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
9837                    + " observer=" + observer);
9838
9839            if (mEnded) {
9840                throw new IllegalStateException("Restore session already ended");
9841            }
9842
9843            if (mTimedOut) {
9844                Slog.i(TAG, "Session already timed out");
9845                return -1;
9846            }
9847
9848            if (mRestoreTransport == null || mRestoreSets == null) {
9849                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9850                return -1;
9851            }
9852
9853            if (mPackageName != null) {
9854                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9855                return -1;
9856            }
9857
9858            String dirName;
9859            try {
9860                dirName = mRestoreTransport.transportDirName();
9861            } catch (RemoteException e) {
9862                // Transport went AWOL; fail.
9863                Slog.e(TAG, "Unable to contact transport for restore");
9864                return -1;
9865            }
9866
9867            synchronized (mQueueLock) {
9868                for (int i = 0; i < mRestoreSets.length; i++) {
9869                    if (token == mRestoreSets[i].token) {
9870                        // Real work, so stop the session timeout until we finalize the restore
9871                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9872
9873                        long oldId = Binder.clearCallingIdentity();
9874                        mWakelock.acquire();
9875                        if (MORE_DEBUG) {
9876                            Slog.d(TAG, "restoreAll() kicking off");
9877                        }
9878                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9879                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
9880                                observer, token);
9881                        mBackupHandler.sendMessage(msg);
9882                        Binder.restoreCallingIdentity(oldId);
9883                        return 0;
9884                    }
9885                }
9886            }
9887
9888            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9889            return -1;
9890        }
9891
9892        // Restores of more than a single package are treated as 'system' restores
9893        public synchronized int restoreSome(long token, IRestoreObserver observer,
9894                String[] packages) {
9895            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9896                    "performRestore");
9897
9898            if (DEBUG) {
9899                StringBuilder b = new StringBuilder(128);
9900                b.append("restoreSome token=");
9901                b.append(Long.toHexString(token));
9902                b.append(" observer=");
9903                b.append(observer.toString());
9904                b.append(" packages=");
9905                if (packages == null) {
9906                    b.append("null");
9907                } else {
9908                    b.append('{');
9909                    boolean first = true;
9910                    for (String s : packages) {
9911                        if (!first) {
9912                            b.append(", ");
9913                        } else first = false;
9914                        b.append(s);
9915                    }
9916                    b.append('}');
9917                }
9918                Slog.d(TAG, b.toString());
9919            }
9920
9921            if (mEnded) {
9922                throw new IllegalStateException("Restore session already ended");
9923            }
9924
9925            if (mTimedOut) {
9926                Slog.i(TAG, "Session already timed out");
9927                return -1;
9928            }
9929
9930            if (mRestoreTransport == null || mRestoreSets == null) {
9931                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9932                return -1;
9933            }
9934
9935            if (mPackageName != null) {
9936                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9937                return -1;
9938            }
9939
9940            String dirName;
9941            try {
9942                dirName = mRestoreTransport.transportDirName();
9943            } catch (RemoteException e) {
9944                // Transport went AWOL; fail.
9945                Slog.e(TAG, "Unable to contact transport for restore");
9946                return -1;
9947            }
9948
9949            synchronized (mQueueLock) {
9950                for (int i = 0; i < mRestoreSets.length; i++) {
9951                    if (token == mRestoreSets[i].token) {
9952                        // Stop the session timeout until we finalize the restore
9953                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9954
9955                        long oldId = Binder.clearCallingIdentity();
9956                        mWakelock.acquire();
9957                        if (MORE_DEBUG) {
9958                            Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
9959                        }
9960                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9961                        msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
9962                                packages, packages.length > 1);
9963                        mBackupHandler.sendMessage(msg);
9964                        Binder.restoreCallingIdentity(oldId);
9965                        return 0;
9966                    }
9967                }
9968            }
9969
9970            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9971            return -1;
9972        }
9973
9974        public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
9975            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
9976
9977            if (mEnded) {
9978                throw new IllegalStateException("Restore session already ended");
9979            }
9980
9981            if (mTimedOut) {
9982                Slog.i(TAG, "Session already timed out");
9983                return -1;
9984            }
9985
9986            if (mPackageName != null) {
9987                if (! mPackageName.equals(packageName)) {
9988                    Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
9989                            + " on session for package " + mPackageName);
9990                    return -1;
9991                }
9992            }
9993
9994            PackageInfo app = null;
9995            try {
9996                app = mPackageManager.getPackageInfo(packageName, 0);
9997            } catch (NameNotFoundException nnf) {
9998                Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9999                return -1;
10000            }
10001
10002            // If the caller is not privileged and is not coming from the target
10003            // app's uid, throw a permission exception back to the caller.
10004            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
10005                    Binder.getCallingPid(), Binder.getCallingUid());
10006            if ((perm == PackageManager.PERMISSION_DENIED) &&
10007                    (app.applicationInfo.uid != Binder.getCallingUid())) {
10008                Slog.w(TAG, "restorePackage: bad packageName=" + packageName
10009                        + " or calling uid=" + Binder.getCallingUid());
10010                throw new SecurityException("No permission to restore other packages");
10011            }
10012
10013            // So far so good; we're allowed to try to restore this package.
10014            long oldId = Binder.clearCallingIdentity();
10015            try {
10016                // Check whether there is data for it in the current dataset, falling back
10017                // to the ancestral dataset if not.
10018                long token = getAvailableRestoreToken(packageName);
10019                if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName
10020                        + " token=" + Long.toHexString(token));
10021
10022                // If we didn't come up with a place to look -- no ancestral dataset and
10023                // the app has never been backed up from this device -- there's nothing
10024                // to do but return failure.
10025                if (token == 0) {
10026                    if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
10027                    return -1;
10028                }
10029
10030                String dirName;
10031                try {
10032                    dirName = mRestoreTransport.transportDirName();
10033                } catch (RemoteException e) {
10034                    // Transport went AWOL; fail.
10035                    Slog.e(TAG, "Unable to contact transport for restore");
10036                    return -1;
10037                }
10038
10039                // Stop the session timeout until we finalize the restore
10040                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
10041
10042                // Ready to go:  enqueue the restore request and claim success
10043                mWakelock.acquire();
10044                if (MORE_DEBUG) {
10045                    Slog.d(TAG, "restorePackage() : " + packageName);
10046                }
10047                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
10048                msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app);
10049                mBackupHandler.sendMessage(msg);
10050            } finally {
10051                Binder.restoreCallingIdentity(oldId);
10052            }
10053            return 0;
10054        }
10055
10056        // Posted to the handler to tear down a restore session in a cleanly synchronized way
10057        class EndRestoreRunnable implements Runnable {
10058            BackupManagerService mBackupManager;
10059            ActiveRestoreSession mSession;
10060
10061            EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
10062                mBackupManager = manager;
10063                mSession = session;
10064            }
10065
10066            public void run() {
10067                // clean up the session's bookkeeping
10068                synchronized (mSession) {
10069                    mSession.mRestoreTransport = null;
10070                    mSession.mEnded = true;
10071                }
10072
10073                // clean up the BackupManagerImpl side of the bookkeeping
10074                // and cancel any pending timeout message
10075                mBackupManager.clearRestoreSession(mSession);
10076            }
10077        }
10078
10079        public synchronized void endRestoreSession() {
10080            if (DEBUG) Slog.d(TAG, "endRestoreSession");
10081
10082            if (mTimedOut) {
10083                Slog.i(TAG, "Session already timed out");
10084                return;
10085            }
10086
10087            if (mEnded) {
10088                throw new IllegalStateException("Restore session already ended");
10089            }
10090
10091            mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
10092        }
10093    }
10094
10095    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
10096        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
10097
10098        long identityToken = Binder.clearCallingIdentity();
10099        try {
10100            if (args != null) {
10101                for (String arg : args) {
10102                    if ("-h".equals(arg)) {
10103                        pw.println("'dumpsys backup' optional arguments:");
10104                        pw.println("  -h       : this help text");
10105                        pw.println("  a[gents] : dump information about defined backup agents");
10106                        return;
10107                    } else if ("agents".startsWith(arg)) {
10108                        dumpAgents(pw);
10109                        return;
10110                    }
10111                }
10112            }
10113            dumpInternal(pw);
10114        } finally {
10115            Binder.restoreCallingIdentity(identityToken);
10116        }
10117    }
10118
10119    private void dumpAgents(PrintWriter pw) {
10120        List<PackageInfo> agentPackages = allAgentPackages();
10121        pw.println("Defined backup agents:");
10122        for (PackageInfo pkg : agentPackages) {
10123            pw.print("  ");
10124            pw.print(pkg.packageName); pw.println(':');
10125            pw.print("      "); pw.println(pkg.applicationInfo.backupAgentName);
10126        }
10127    }
10128
10129    private void dumpInternal(PrintWriter pw) {
10130        synchronized (mQueueLock) {
10131            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
10132                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
10133                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
10134            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
10135            if (mBackupRunning) pw.println("Backup currently running");
10136            pw.println("Last backup pass started: " + mLastBackupPass
10137                    + " (now = " + System.currentTimeMillis() + ')');
10138            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
10139
10140            pw.println("Available transports:");
10141            final String[] transports = listAllTransports();
10142            if (transports != null) {
10143                for (String t : listAllTransports()) {
10144                    pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
10145                    try {
10146                        IBackupTransport transport = getTransport(t);
10147                        File dir = new File(mBaseStateDir, transport.transportDirName());
10148                        pw.println("       destination: " + transport.currentDestinationString());
10149                        pw.println("       intent: " + transport.configurationIntent());
10150                        for (File f : dir.listFiles()) {
10151                            pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
10152                        }
10153                    } catch (Exception e) {
10154                        Slog.e(TAG, "Error in transport", e);
10155                        pw.println("        Error: " + e);
10156                    }
10157                }
10158            }
10159
10160            pw.println("Pending init: " + mPendingInits.size());
10161            for (String s : mPendingInits) {
10162                pw.println("    " + s);
10163            }
10164
10165            if (DEBUG_BACKUP_TRACE) {
10166                synchronized (mBackupTrace) {
10167                    if (!mBackupTrace.isEmpty()) {
10168                        pw.println("Most recent backup trace:");
10169                        for (String s : mBackupTrace) {
10170                            pw.println("   " + s);
10171                        }
10172                    }
10173                }
10174            }
10175
10176            pw.print("Ancestral: "); pw.println(Long.toHexString(mAncestralToken));
10177            pw.print("Current:   "); pw.println(Long.toHexString(mCurrentToken));
10178
10179            int N = mBackupParticipants.size();
10180            pw.println("Participants:");
10181            for (int i=0; i<N; i++) {
10182                int uid = mBackupParticipants.keyAt(i);
10183                pw.print("  uid: ");
10184                pw.println(uid);
10185                HashSet<String> participants = mBackupParticipants.valueAt(i);
10186                for (String app: participants) {
10187                    pw.println("    " + app);
10188                }
10189            }
10190
10191            pw.println("Ancestral packages: "
10192                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
10193            if (mAncestralPackages != null) {
10194                for (String pkg : mAncestralPackages) {
10195                    pw.println("    " + pkg);
10196                }
10197            }
10198
10199            pw.println("Ever backed up: " + mEverStoredApps.size());
10200            for (String pkg : mEverStoredApps) {
10201                pw.println("    " + pkg);
10202            }
10203
10204            pw.println("Pending key/value backup: " + mPendingBackups.size());
10205            for (BackupRequest req : mPendingBackups.values()) {
10206                pw.println("    " + req);
10207            }
10208
10209            pw.println("Full backup queue:" + mFullBackupQueue.size());
10210            for (FullBackupEntry entry : mFullBackupQueue) {
10211                pw.print("    "); pw.print(entry.lastBackup);
10212                pw.print(" : "); pw.println(entry.packageName);
10213            }
10214        }
10215    }
10216
10217    private static void sendBackupOnUpdate(IBackupObserver observer, String packageName,
10218            BackupProgress progress) {
10219        if (observer != null) {
10220            try {
10221                observer.onUpdate(packageName, progress);
10222            } catch (RemoteException e) {
10223                if (DEBUG) {
10224                    Slog.w(TAG, "Backup observer went away: onUpdate");
10225                }
10226            }
10227        }
10228    }
10229
10230    private static void sendBackupOnPackageResult(IBackupObserver observer, String packageName,
10231            int status) {
10232        if (observer != null) {
10233            try {
10234                observer.onResult(packageName, status);
10235            } catch (RemoteException e) {
10236                if (DEBUG) {
10237                    Slog.w(TAG, "Backup observer went away: onResult");
10238                }
10239            }
10240        }
10241    }
10242
10243    private static void sendBackupFinished(IBackupObserver observer, int status) {
10244        if (observer != null) {
10245            try {
10246                observer.backupFinished(status);
10247            } catch (RemoteException e) {
10248                if (DEBUG) {
10249                    Slog.w(TAG, "Backup observer went away: backupFinished");
10250                }
10251            }
10252        }
10253    }
10254}
10255