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