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