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