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