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