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