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