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