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