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