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