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