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