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