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