BackupManagerService.java revision ab06997ed5d4599f7223f3033b5c3bc77ac7044e
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                // Set up to send data to the transport
3942                final int N = mPackages.size();
3943                for (int i = 0; i < N; i++) {
3944                    currentPackage = mPackages.get(i);
3945                    if (DEBUG) {
3946                        Slog.i(TAG, "Initiating full-data transport backup of "
3947                                + currentPackage.packageName);
3948                    }
3949                    EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE,
3950                            currentPackage.packageName);
3951
3952                    transportPipes = ParcelFileDescriptor.createPipe();
3953
3954                    // Tell the transport the data's coming
3955                    int result = transport.performFullBackup(currentPackage,
3956                            transportPipes[0]);
3957                    if (result == BackupTransport.TRANSPORT_OK) {
3958                        // The transport has its own copy of the read end of the pipe,
3959                        // so close ours now
3960                        transportPipes[0].close();
3961                        transportPipes[0] = null;
3962
3963                        // Now set up the backup engine / data source end of things
3964                        enginePipes = ParcelFileDescriptor.createPipe();
3965                        CountDownLatch runnerLatch = new CountDownLatch(1);
3966                        SinglePackageBackupRunner backupRunner =
3967                                new SinglePackageBackupRunner(enginePipes[1], currentPackage,
3968                                        transport, runnerLatch);
3969                        // The runner dup'd the pipe half, so we close it here
3970                        enginePipes[1].close();
3971                        enginePipes[1] = null;
3972
3973                        // Spin off the runner to fetch the app's data and pipe it
3974                        // into the engine pipes
3975                        (new Thread(backupRunner, "package-backup-bridge")).start();
3976
3977                        // Read data off the engine pipe and pass it to the transport
3978                        // pipe until we hit EOD on the input stream.
3979                        FileInputStream in = new FileInputStream(
3980                                enginePipes[0].getFileDescriptor());
3981                        FileOutputStream out = new FileOutputStream(
3982                                transportPipes[1].getFileDescriptor());
3983                        byte[] buffer = new byte[8192];
3984                        int nRead = 0;
3985                        do {
3986                            if (!mKeepRunning.get()) {
3987                                if (DEBUG_SCHEDULING) {
3988                                    Slog.i(TAG, "Full backup task told to stop");
3989                                }
3990                                break;
3991                            }
3992                            nRead = in.read(buffer);
3993                            if (MORE_DEBUG) {
3994                                Slog.v(TAG, "in.read(buffer) from app: " + nRead);
3995                            }
3996                            if (nRead > 0) {
3997                                out.write(buffer, 0, nRead);
3998                                result = transport.sendBackupData(nRead);
3999                            }
4000                        } while (nRead > 0 && result == BackupTransport.TRANSPORT_OK);
4001
4002                        // If we've lost our running criteria, tell the transport to cancel
4003                        // and roll back this (partial) backup payload; otherwise tell it
4004                        // that we've reached the clean finish state.
4005                        if (!mKeepRunning.get()) {
4006                            result = BackupTransport.TRANSPORT_ERROR;
4007                            transport.cancelFullBackup();
4008                        } else {
4009                            // If we were otherwise in a good state, now interpret the final
4010                            // result based on what finishBackup() returns.  If we're in a
4011                            // failure case already, preserve that result and ignore whatever
4012                            // finishBackup() reports.
4013                            final int finishResult = transport.finishBackup();
4014                            if (result == BackupTransport.TRANSPORT_OK) {
4015                                result = finishResult;
4016                            }
4017                        }
4018
4019                        if (MORE_DEBUG) {
4020                            Slog.i(TAG, "Done trying to send backup data: result=" + result);
4021                        }
4022
4023                        if (result != BackupTransport.TRANSPORT_OK) {
4024                            Slog.e(TAG, "Error " + result
4025                                    + " backing up " + currentPackage.packageName);
4026                        }
4027
4028                        // Also ask the transport how long it wants us to wait before
4029                        // moving on to the next package, if any.
4030                        backoff = transport.requestFullBackupTime();
4031                        if (DEBUG_SCHEDULING) {
4032                            Slog.i(TAG, "Transport suggested backoff=" + backoff);
4033                        }
4034
4035                    }
4036
4037                    // Roll this package to the end of the backup queue if we're
4038                    // in a queue-driven mode (regardless of success/failure)
4039                    if (mUpdateSchedule) {
4040                        enqueueFullBackup(currentPackage.packageName,
4041                                System.currentTimeMillis());
4042                    }
4043
4044                    if (result == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
4045                        if (DEBUG) {
4046                            Slog.i(TAG, "Transport rejected backup of "
4047                                    + currentPackage.packageName
4048                                    + ", skipping");
4049                        }
4050                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE,
4051                                currentPackage.packageName, "transport rejected");
4052                        // do nothing, clean up, and continue looping
4053                    } else if (result != BackupTransport.TRANSPORT_OK) {
4054                        if (DEBUG) {
4055                            Slog.i(TAG, "Transport failed; aborting backup: " + result);
4056                            EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
4057                            return;
4058                        }
4059                    } else {
4060                        // Success!
4061                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS,
4062                                currentPackage.packageName);
4063                        logBackupComplete(currentPackage.packageName);
4064                    }
4065                    cleanUpPipes(transportPipes);
4066                    cleanUpPipes(enginePipes);
4067                    currentPackage = null;
4068                }
4069
4070                if (DEBUG) {
4071                    Slog.i(TAG, "Full backup completed.");
4072                }
4073            } catch (Exception e) {
4074                Slog.w(TAG, "Exception trying full transport backup", e);
4075            } finally {
4076                cleanUpPipes(transportPipes);
4077                cleanUpPipes(enginePipes);
4078
4079                if (mJob != null) {
4080                    mJob.finishBackupPass();
4081                }
4082
4083                synchronized (mQueueLock) {
4084                    mRunningFullBackupTask = null;
4085                }
4086
4087                mLatch.countDown();
4088
4089                // Now that we're actually done with schedule-driven work, reschedule
4090                // the next pass based on the new queue state.
4091                if (mUpdateSchedule) {
4092                    scheduleNextFullBackupJob(backoff);
4093                }
4094            }
4095        }
4096
4097        void cleanUpPipes(ParcelFileDescriptor[] pipes) {
4098            if (pipes != null) {
4099                if (pipes[0] != null) {
4100                    ParcelFileDescriptor fd = pipes[0];
4101                    pipes[0] = null;
4102                    try {
4103                        fd.close();
4104                    } catch (IOException e) {
4105                        Slog.w(TAG, "Unable to close pipe!");
4106                    }
4107                }
4108                if (pipes[1] != null) {
4109                    ParcelFileDescriptor fd = pipes[1];
4110                    pipes[1] = null;
4111                    try {
4112                        fd.close();
4113                    } catch (IOException e) {
4114                        Slog.w(TAG, "Unable to close pipe!");
4115                    }
4116                }
4117            }
4118        }
4119
4120        // Run the backup and pipe it back to the given socket -- expects to run on
4121        // a standalone thread.  The  runner owns this half of the pipe, and closes
4122        // it to indicate EOD to the other end.
4123        class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
4124            final AtomicInteger mResult = new AtomicInteger();
4125            final CountDownLatch mLatch = new CountDownLatch(1);
4126            final IBackupTransport mTransport;
4127
4128            public SinglePackageBackupPreflight(IBackupTransport transport) {
4129                mTransport = transport;
4130            }
4131
4132            @Override
4133            public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
4134                int result;
4135                try {
4136                    final int token = generateToken();
4137                    prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this);
4138                    addBackupTrace("preflighting");
4139                    if (MORE_DEBUG) {
4140                        Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
4141                    }
4142                    agent.doMeasureFullBackup(token, mBackupManagerBinder);
4143
4144                    // now wait to get our result back
4145                    mLatch.await();
4146                    int totalSize = mResult.get();
4147                    if (MORE_DEBUG) {
4148                        Slog.v(TAG, "Got preflight response; size=" + totalSize);
4149                    }
4150
4151                    result = mTransport.checkFullBackupSize(totalSize);
4152                } catch (Exception e) {
4153                    Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
4154                    result = BackupTransport.AGENT_ERROR;
4155                }
4156                return result;
4157            }
4158
4159            @Override
4160            public void execute() {
4161                // Unused in this case
4162            }
4163
4164            @Override
4165            public void operationComplete(int result) {
4166                // got the callback, and our preflightFullBackup() method is waiting for the result
4167                if (MORE_DEBUG) {
4168                    Slog.i(TAG, "Preflight op complete, result=" + result);
4169                }
4170                mResult.set(result);
4171                mLatch.countDown();
4172            }
4173
4174            @Override
4175            public void handleTimeout() {
4176                if (MORE_DEBUG) {
4177                    Slog.i(TAG, "Preflight timeout; failing");
4178                }
4179                mResult.set(BackupTransport.AGENT_ERROR);
4180                mLatch.countDown();
4181            }
4182
4183        }
4184
4185        class SinglePackageBackupRunner implements Runnable {
4186            final ParcelFileDescriptor mOutput;
4187            final PackageInfo mTarget;
4188            final FullBackupPreflight mPreflight;
4189            final CountDownLatch mLatch;
4190
4191            SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
4192                    IBackupTransport transport, CountDownLatch latch) throws IOException {
4193                mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
4194                mTarget = target;
4195                mPreflight = new SinglePackageBackupPreflight(transport);
4196                mLatch = latch;
4197            }
4198
4199            @Override
4200            public void run() {
4201                try {
4202                    FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
4203                    FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName,
4204                            mPreflight, false);
4205                    engine.backupOnePackage(mTarget);
4206                } catch (Exception e) {
4207                    Slog.e(TAG, "Exception during full package backup of " + mTarget);
4208                } finally {
4209                    mLatch.countDown();
4210                    try {
4211                        mOutput.close();
4212                    } catch (IOException e) {
4213                        Slog.w(TAG, "Error closing transport pipe in runner");
4214                    }
4215                }
4216            }
4217        }
4218    }
4219
4220    // ----- Full-data backup scheduling -----
4221
4222    /**
4223     * Schedule a job to tell us when it's a good time to run a full backup
4224     */
4225    void scheduleNextFullBackupJob(long transportMinLatency) {
4226        synchronized (mQueueLock) {
4227            if (mFullBackupQueue.size() > 0) {
4228                // schedule the next job at the point in the future when the least-recently
4229                // backed up app comes due for backup again; or immediately if it's already
4230                // due.
4231                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
4232                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
4233                final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
4234                        ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
4235                final long latency = Math.min(transportMinLatency, appLatency);
4236                Runnable r = new Runnable() {
4237                    @Override public void run() {
4238                        FullBackupJob.schedule(mContext, latency);
4239                    }
4240                };
4241                mBackupHandler.postDelayed(r, 2500);
4242            } else {
4243                if (DEBUG_SCHEDULING) {
4244                    Slog.i(TAG, "Full backup queue empty; not scheduling");
4245                }
4246            }
4247        }
4248    }
4249
4250    /**
4251     * Enqueue full backup for the given app, with a note about when it last ran.
4252     */
4253    void enqueueFullBackup(String packageName, long lastBackedUp) {
4254        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
4255        synchronized (mQueueLock) {
4256            int N = mFullBackupQueue.size();
4257            // First, sanity check that we aren't adding a duplicate.  Slow but
4258            // straightforward; we'll have at most on the order of a few hundred
4259            // items in this list.
4260            for (int i = N-1; i >= 0; i--) {
4261                final FullBackupEntry e = mFullBackupQueue.get(i);
4262                if (packageName.equals(e.packageName)) {
4263                    if (DEBUG) {
4264                        Slog.w(TAG, "Removing schedule queue dupe of " + packageName);
4265                    }
4266                    mFullBackupQueue.remove(i);
4267                }
4268            }
4269
4270            // This is also slow but easy for modest numbers of apps: work backwards
4271            // from the end of the queue until we find an item whose last backup
4272            // time was before this one, then insert this new entry after it.
4273            int which;
4274            for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
4275                final FullBackupEntry entry = mFullBackupQueue.get(which);
4276                if (entry.lastBackup <= lastBackedUp) {
4277                    mFullBackupQueue.add(which + 1, newEntry);
4278                    break;
4279                }
4280            }
4281            if (which < 0) {
4282                // this one is earlier than any existing one, so prepend
4283                mFullBackupQueue.add(0, newEntry);
4284            }
4285        }
4286        writeFullBackupScheduleAsync();
4287    }
4288
4289    private boolean fullBackupAllowable(IBackupTransport transport) {
4290        if (transport == null) {
4291            Slog.w(TAG, "Transport not present; full data backup not performed");
4292            return false;
4293        }
4294
4295        // Don't proceed unless we have already established package metadata
4296        // for the current dataset via a key/value backup pass.
4297        try {
4298            File stateDir = new File(mBaseStateDir, transport.transportDirName());
4299            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
4300            if (pmState.length() <= 0) {
4301                if (DEBUG) {
4302                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
4303                }
4304                return false;
4305            }
4306        } catch (Exception e) {
4307            Slog.w(TAG, "Unable to contact transport");
4308            return false;
4309        }
4310
4311        return true;
4312    }
4313
4314    /**
4315     * Conditions are right for a full backup operation, so run one.  The model we use is
4316     * to perform one app backup per scheduled job execution, and to reschedule the job
4317     * with zero latency as long as conditions remain right and we still have work to do.
4318     *
4319     * @return Whether ongoing work will continue.  The return value here will be passed
4320     *         along as the return value to the scheduled job's onStartJob() callback.
4321     */
4322    boolean beginFullBackup(FullBackupJob scheduledJob) {
4323        long now = System.currentTimeMillis();
4324        FullBackupEntry entry = null;
4325        long latency = MIN_FULL_BACKUP_INTERVAL;
4326
4327        if (!mEnabled || !mProvisioned) {
4328            // Backups are globally disabled, so don't proceed.  We also don't reschedule
4329            // the job driving automatic backups; that job will be scheduled again when
4330            // the user enables backup.
4331            if (MORE_DEBUG) {
4332                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
4333                        + " p=" + mProvisioned + "; ignoring");
4334            }
4335            return false;
4336        }
4337
4338        if (DEBUG_SCHEDULING) {
4339            Slog.i(TAG, "Beginning scheduled full backup operation");
4340        }
4341
4342        // Great; we're able to run full backup jobs now.  See if we have any work to do.
4343        synchronized (mQueueLock) {
4344            if (mRunningFullBackupTask != null) {
4345                Slog.e(TAG, "Backup triggered but one already/still running!");
4346                return false;
4347            }
4348
4349            if (mFullBackupQueue.size() == 0) {
4350                // no work to do so just bow out
4351                if (DEBUG) {
4352                    Slog.i(TAG, "Backup queue empty; doing nothing");
4353                }
4354                return false;
4355            }
4356
4357            // At this point we know that we have work to do, just not right now.  Any
4358            // exit without actually running backups will also require that we
4359            // reschedule the job.
4360            boolean runBackup = true;
4361
4362            if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
4363                if (MORE_DEBUG) {
4364                    Slog.i(TAG, "Preconditions not met; not running full backup");
4365                }
4366                runBackup = false;
4367                // Typically this means we haven't run a key/value backup yet.  Back off
4368                // full-backup operations by the key/value job's run interval so that
4369                // next time we run, we are likely to be able to make progress.
4370                latency = KeyValueBackupJob.BATCH_INTERVAL;
4371            }
4372
4373            if (runBackup) {
4374                entry = mFullBackupQueue.get(0);
4375                long timeSinceRun = now - entry.lastBackup;
4376                runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
4377                if (!runBackup) {
4378                    // It's too early to back up the next thing in the queue, so bow out
4379                    if (MORE_DEBUG) {
4380                        Slog.i(TAG, "Device ready but too early to back up next app");
4381                    }
4382                    // Wait until the next app in the queue falls due for a full data backup
4383                    latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
4384                }
4385            }
4386
4387            if (!runBackup) {
4388                final long deferTime = latency;     // pin for the closure
4389                mBackupHandler.post(new Runnable() {
4390                    @Override public void run() {
4391                        FullBackupJob.schedule(mContext, deferTime);
4392                    }
4393                });
4394                return false;
4395            }
4396
4397            // Okay, the top thing is runnable now.  Pop it off and get going.
4398            mFullBackupQueue.remove(0);
4399            CountDownLatch latch = new CountDownLatch(1);
4400            String[] pkg = new String[] {entry.packageName};
4401            mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
4402                    scheduledJob, latch);
4403            (new Thread(mRunningFullBackupTask)).start();
4404        }
4405
4406        return true;
4407    }
4408
4409    // The job scheduler says our constraints don't hold any more,
4410    // so tear down any ongoing backup task right away.
4411    void endFullBackup() {
4412        synchronized (mQueueLock) {
4413            if (mRunningFullBackupTask != null) {
4414                if (DEBUG_SCHEDULING) {
4415                    Slog.i(TAG, "Telling running backup to stop");
4416                }
4417                mRunningFullBackupTask.setRunning(false);
4418            }
4419        }
4420    }
4421
4422    // ----- Restore infrastructure -----
4423
4424    abstract class RestoreEngine {
4425        static final String TAG = "RestoreEngine";
4426
4427        public static final int SUCCESS = 0;
4428        public static final int TARGET_FAILURE = -2;
4429        public static final int TRANSPORT_FAILURE = -3;
4430
4431        private AtomicBoolean mRunning = new AtomicBoolean(false);
4432        private AtomicInteger mResult = new AtomicInteger(SUCCESS);
4433
4434        public boolean isRunning() {
4435            return mRunning.get();
4436        }
4437
4438        public void setRunning(boolean stillRunning) {
4439            synchronized (mRunning) {
4440                mRunning.set(stillRunning);
4441                mRunning.notifyAll();
4442            }
4443        }
4444
4445        public int waitForResult() {
4446            synchronized (mRunning) {
4447                while (isRunning()) {
4448                    try {
4449                        mRunning.wait();
4450                    } catch (InterruptedException e) {}
4451                }
4452            }
4453            return getResult();
4454        }
4455
4456        public int getResult() {
4457            return mResult.get();
4458        }
4459
4460        public void setResult(int result) {
4461            mResult.set(result);
4462        }
4463
4464        // TODO: abstract restore state and APIs
4465    }
4466
4467    // ----- Full restore from a file/socket -----
4468
4469    // Description of a file in the restore datastream
4470    static class FileMetadata {
4471        String packageName;             // name of the owning app
4472        String installerPackageName;    // name of the market-type app that installed the owner
4473        int type;                       // e.g. BackupAgent.TYPE_DIRECTORY
4474        String domain;                  // e.g. FullBackup.DATABASE_TREE_TOKEN
4475        String path;                    // subpath within the semantic domain
4476        long mode;                      // e.g. 0666 (actually int)
4477        long mtime;                     // last mod time, UTC time_t (actually int)
4478        long size;                      // bytes of content
4479
4480        @Override
4481        public String toString() {
4482            StringBuilder sb = new StringBuilder(128);
4483            sb.append("FileMetadata{");
4484            sb.append(packageName); sb.append(',');
4485            sb.append(type); sb.append(',');
4486            sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
4487            sb.append(size);
4488            sb.append('}');
4489            return sb.toString();
4490        }
4491    }
4492
4493    enum RestorePolicy {
4494        IGNORE,
4495        ACCEPT,
4496        ACCEPT_IF_APK
4497    }
4498
4499    // Full restore engine, used by both adb restore and transport-based full restore
4500    class FullRestoreEngine extends RestoreEngine {
4501        // Dedicated observer, if any
4502        IFullBackupRestoreObserver mObserver;
4503
4504        // Where we're delivering the file data as we go
4505        IBackupAgent mAgent;
4506
4507        // Are we permitted to only deliver a specific package's metadata?
4508        PackageInfo mOnlyPackage;
4509
4510        boolean mAllowApks;
4511        boolean mAllowObbs;
4512
4513        // Which package are we currently handling data for?
4514        String mAgentPackage;
4515
4516        // Info for working with the target app process
4517        ApplicationInfo mTargetApp;
4518
4519        // Machinery for restoring OBBs
4520        FullBackupObbConnection mObbConnection = null;
4521
4522        // possible handling states for a given package in the restore dataset
4523        final HashMap<String, RestorePolicy> mPackagePolicies
4524                = new HashMap<String, RestorePolicy>();
4525
4526        // installer package names for each encountered app, derived from the manifests
4527        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
4528
4529        // Signatures for a given package found in its manifest file
4530        final HashMap<String, Signature[]> mManifestSignatures
4531                = new HashMap<String, Signature[]>();
4532
4533        // Packages we've already wiped data on when restoring their first file
4534        final HashSet<String> mClearedPackages = new HashSet<String>();
4535
4536        // How much data have we moved?
4537        long mBytes;
4538
4539        // Working buffer
4540        byte[] mBuffer;
4541
4542        // Pipes for moving data
4543        ParcelFileDescriptor[] mPipes = null;
4544
4545        // Widget blob to be restored out-of-band
4546        byte[] mWidgetData = null;
4547
4548        // Runner that can be placed in a separate thread to do in-process
4549        // invocations of the full restore API asynchronously
4550        class RestoreFileRunnable implements Runnable {
4551            IBackupAgent mAgent;
4552            FileMetadata mInfo;
4553            ParcelFileDescriptor mSocket;
4554            int mToken;
4555
4556            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
4557                    ParcelFileDescriptor socket, int token) throws IOException {
4558                mAgent = agent;
4559                mInfo = info;
4560                mToken = token;
4561
4562                // This class is used strictly for process-local binder invocations.  The
4563                // semantics of ParcelFileDescriptor differ in this case; in particular, we
4564                // do not automatically get a 'dup'ed descriptor that we can can continue
4565                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
4566                // before proceeding to do the restore.
4567                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
4568            }
4569
4570            @Override
4571            public void run() {
4572                try {
4573                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
4574                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
4575                            mToken, mBackupManagerBinder);
4576                } catch (RemoteException e) {
4577                    // never happens; this is used strictly for local binder calls
4578                }
4579            }
4580        }
4581
4582        public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage,
4583                boolean allowApks, boolean allowObbs) {
4584            mObserver = observer;
4585            mOnlyPackage = onlyPackage;
4586            mAllowApks = allowApks;
4587            mAllowObbs = allowObbs;
4588            mBuffer = new byte[32 * 1024];
4589            mBytes = 0;
4590        }
4591
4592        public boolean restoreOneFile(InputStream instream) {
4593            if (!isRunning()) {
4594                Slog.w(TAG, "Restore engine used after halting");
4595                return false;
4596            }
4597
4598            FileMetadata info;
4599            try {
4600                if (MORE_DEBUG) {
4601                    Slog.v(TAG, "Reading tar header for restoring file");
4602                }
4603                info = readTarHeaders(instream);
4604                if (info != null) {
4605                    if (MORE_DEBUG) {
4606                        dumpFileMetadata(info);
4607                    }
4608
4609                    final String pkg = info.packageName;
4610                    if (!pkg.equals(mAgentPackage)) {
4611                        // In the single-package case, it's a semantic error to expect
4612                        // one app's data but see a different app's on the wire
4613                        if (mOnlyPackage != null) {
4614                            if (!pkg.equals(mOnlyPackage.packageName)) {
4615                                Slog.w(TAG, "Expected data for " + mOnlyPackage
4616                                        + " but saw " + pkg);
4617                                setResult(RestoreEngine.TRANSPORT_FAILURE);
4618                                setRunning(false);
4619                                return false;
4620                            }
4621                        }
4622
4623                        // okay, change in package; set up our various
4624                        // bookkeeping if we haven't seen it yet
4625                        if (!mPackagePolicies.containsKey(pkg)) {
4626                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4627                        }
4628
4629                        // Clean up the previous agent relationship if necessary,
4630                        // and let the observer know we're considering a new app.
4631                        if (mAgent != null) {
4632                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
4633                            // Now we're really done
4634                            tearDownPipes();
4635                            tearDownAgent(mTargetApp);
4636                            mTargetApp = null;
4637                            mAgentPackage = null;
4638                        }
4639                    }
4640
4641                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
4642                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
4643                        mPackageInstallers.put(pkg, info.installerPackageName);
4644                        // We've read only the manifest content itself at this point,
4645                        // so consume the footer before looping around to the next
4646                        // input file
4647                        skipTarPadding(info.size, instream);
4648                        sendOnRestorePackage(pkg);
4649                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
4650                        // Metadata blobs!
4651                        readMetadata(info, instream);
4652                        skipTarPadding(info.size, instream);
4653                    } else {
4654                        // Non-manifest, so it's actual file data.  Is this a package
4655                        // we're ignoring?
4656                        boolean okay = true;
4657                        RestorePolicy policy = mPackagePolicies.get(pkg);
4658                        switch (policy) {
4659                            case IGNORE:
4660                                okay = false;
4661                                break;
4662
4663                            case ACCEPT_IF_APK:
4664                                // If we're in accept-if-apk state, then the first file we
4665                                // see MUST be the apk.
4666                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
4667                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
4668                                    // Try to install the app.
4669                                    String installerName = mPackageInstallers.get(pkg);
4670                                    okay = installApk(info, installerName, instream);
4671                                    // good to go; promote to ACCEPT
4672                                    mPackagePolicies.put(pkg, (okay)
4673                                            ? RestorePolicy.ACCEPT
4674                                                    : RestorePolicy.IGNORE);
4675                                    // At this point we've consumed this file entry
4676                                    // ourselves, so just strip the tar footer and
4677                                    // go on to the next file in the input stream
4678                                    skipTarPadding(info.size, instream);
4679                                    return true;
4680                                } else {
4681                                    // File data before (or without) the apk.  We can't
4682                                    // handle it coherently in this case so ignore it.
4683                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4684                                    okay = false;
4685                                }
4686                                break;
4687
4688                            case ACCEPT:
4689                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
4690                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
4691                                    // we can take the data without the apk, so we
4692                                    // *want* to do so.  skip the apk by declaring this
4693                                    // one file not-okay without changing the restore
4694                                    // policy for the package.
4695                                    okay = false;
4696                                }
4697                                break;
4698
4699                            default:
4700                                // Something has gone dreadfully wrong when determining
4701                                // the restore policy from the manifest.  Ignore the
4702                                // rest of this package's data.
4703                                Slog.e(TAG, "Invalid policy from manifest");
4704                                okay = false;
4705                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4706                                break;
4707                        }
4708
4709                        // Is it a *file* we need to drop?
4710                        if (!isRestorableFile(info)) {
4711                            okay = false;
4712                        }
4713
4714                        // If the policy is satisfied, go ahead and set up to pipe the
4715                        // data to the agent.
4716                        if (DEBUG && okay && mAgent != null) {
4717                            Slog.i(TAG, "Reusing existing agent instance");
4718                        }
4719                        if (okay && mAgent == null) {
4720                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
4721
4722                            try {
4723                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
4724
4725                                // If we haven't sent any data to this app yet, we probably
4726                                // need to clear it first.  Check that.
4727                                if (!mClearedPackages.contains(pkg)) {
4728                                    // apps with their own backup agents are
4729                                    // responsible for coherently managing a full
4730                                    // restore.
4731                                    if (mTargetApp.backupAgentName == null) {
4732                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
4733                                        clearApplicationDataSynchronous(pkg);
4734                                    } else {
4735                                        if (DEBUG) Slog.d(TAG, "backup agent ("
4736                                                + mTargetApp.backupAgentName + ") => no clear");
4737                                    }
4738                                    mClearedPackages.add(pkg);
4739                                } else {
4740                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
4741                                }
4742
4743                                // All set; now set up the IPC and launch the agent
4744                                setUpPipes();
4745                                mAgent = bindToAgentSynchronous(mTargetApp,
4746                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
4747                                mAgentPackage = pkg;
4748                            } catch (IOException e) {
4749                                // fall through to error handling
4750                            } catch (NameNotFoundException e) {
4751                                // fall through to error handling
4752                            }
4753
4754                            if (mAgent == null) {
4755                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
4756                                okay = false;
4757                                tearDownPipes();
4758                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4759                            }
4760                        }
4761
4762                        // Sanity check: make sure we never give data to the wrong app.  This
4763                        // should never happen but a little paranoia here won't go amiss.
4764                        if (okay && !pkg.equals(mAgentPackage)) {
4765                            Slog.e(TAG, "Restoring data for " + pkg
4766                                    + " but agent is for " + mAgentPackage);
4767                            okay = false;
4768                        }
4769
4770                        // At this point we have an agent ready to handle the full
4771                        // restore data as well as a pipe for sending data to
4772                        // that agent.  Tell the agent to start reading from the
4773                        // pipe.
4774                        if (okay) {
4775                            boolean agentSuccess = true;
4776                            long toCopy = info.size;
4777                            final int token = generateToken();
4778                            try {
4779                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
4780                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
4781                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
4782                                            + " : " + info.path);
4783                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
4784                                            info.size, info.type, info.path, info.mode,
4785                                            info.mtime, token, mBackupManagerBinder);
4786                                } else {
4787                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
4788                                            + info.path);
4789                                    // fire up the app's agent listening on the socket.  If
4790                                    // the agent is running in the system process we can't
4791                                    // just invoke it asynchronously, so we provide a thread
4792                                    // for it here.
4793                                    if (mTargetApp.processName.equals("system")) {
4794                                        Slog.d(TAG, "system process agent - spinning a thread");
4795                                        RestoreFileRunnable runner = new RestoreFileRunnable(
4796                                                mAgent, info, mPipes[0], token);
4797                                        new Thread(runner, "restore-sys-runner").start();
4798                                    } else {
4799                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
4800                                                info.domain, info.path, info.mode, info.mtime,
4801                                                token, mBackupManagerBinder);
4802                                    }
4803                                }
4804                            } catch (IOException e) {
4805                                // couldn't dup the socket for a process-local restore
4806                                Slog.d(TAG, "Couldn't establish restore");
4807                                agentSuccess = false;
4808                                okay = false;
4809                            } catch (RemoteException e) {
4810                                // whoops, remote entity went away.  We'll eat the content
4811                                // ourselves, then, and not copy it over.
4812                                Slog.e(TAG, "Agent crashed during full restore");
4813                                agentSuccess = false;
4814                                okay = false;
4815                            }
4816
4817                            // Copy over the data if the agent is still good
4818                            if (okay) {
4819                                if (MORE_DEBUG) {
4820                                    Slog.v(TAG, "  copying to restore agent: "
4821                                            + toCopy + " bytes");
4822                                }
4823                                boolean pipeOkay = true;
4824                                FileOutputStream pipe = new FileOutputStream(
4825                                        mPipes[1].getFileDescriptor());
4826                                while (toCopy > 0) {
4827                                    int toRead = (toCopy > mBuffer.length)
4828                                            ? mBuffer.length : (int)toCopy;
4829                                    int nRead = instream.read(mBuffer, 0, toRead);
4830                                    if (nRead >= 0) mBytes += nRead;
4831                                    if (nRead <= 0) break;
4832                                    toCopy -= nRead;
4833
4834                                    // send it to the output pipe as long as things
4835                                    // are still good
4836                                    if (pipeOkay) {
4837                                        try {
4838                                            pipe.write(mBuffer, 0, nRead);
4839                                        } catch (IOException e) {
4840                                            Slog.e(TAG, "Failed to write to restore pipe", e);
4841                                            pipeOkay = false;
4842                                        }
4843                                    }
4844                                }
4845
4846                                // done sending that file!  Now we just need to consume
4847                                // the delta from info.size to the end of block.
4848                                skipTarPadding(info.size, instream);
4849
4850                                // and now that we've sent it all, wait for the remote
4851                                // side to acknowledge receipt
4852                                agentSuccess = waitUntilOperationComplete(token);
4853                            }
4854
4855                            // okay, if the remote end failed at any point, deal with
4856                            // it by ignoring the rest of the restore on it
4857                            if (!agentSuccess) {
4858                                if (DEBUG) {
4859                                    Slog.i(TAG, "Agent failure; ending restore");
4860                                }
4861                                mBackupHandler.removeMessages(MSG_TIMEOUT);
4862                                tearDownPipes();
4863                                tearDownAgent(mTargetApp);
4864                                mAgent = null;
4865                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4866
4867                                // If this was a single-package restore, we halt immediately
4868                                // with an agent error under these circumstances
4869                                if (mOnlyPackage != null) {
4870                                    setResult(RestoreEngine.TARGET_FAILURE);
4871                                    setRunning(false);
4872                                    return false;
4873                                }
4874                            }
4875                        }
4876
4877                        // Problems setting up the agent communication, an explicitly
4878                        // dropped file, or an already-ignored package: skip to the
4879                        // next stream entry by reading and discarding this file.
4880                        if (!okay) {
4881                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
4882                            long bytesToConsume = (info.size + 511) & ~511;
4883                            while (bytesToConsume > 0) {
4884                                int toRead = (bytesToConsume > mBuffer.length)
4885                                        ? mBuffer.length : (int)bytesToConsume;
4886                                long nRead = instream.read(mBuffer, 0, toRead);
4887                                if (nRead >= 0) mBytes += nRead;
4888                                if (nRead <= 0) break;
4889                                bytesToConsume -= nRead;
4890                            }
4891                        }
4892                    }
4893                }
4894            } catch (IOException e) {
4895                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
4896                setResult(RestoreEngine.TRANSPORT_FAILURE);
4897                info = null;
4898            }
4899
4900            // If we got here we're either running smoothly or we've finished
4901            if (info == null) {
4902                if (MORE_DEBUG) {
4903                    Slog.i(TAG, "No [more] data for this package; tearing down");
4904                }
4905                tearDownPipes();
4906                tearDownAgent(mTargetApp);
4907                setRunning(false);
4908            }
4909            return (info != null);
4910        }
4911
4912        void setUpPipes() throws IOException {
4913            mPipes = ParcelFileDescriptor.createPipe();
4914        }
4915
4916        void tearDownPipes() {
4917            if (mPipes != null) {
4918                try {
4919                    mPipes[0].close();
4920                    mPipes[0] = null;
4921                    mPipes[1].close();
4922                    mPipes[1] = null;
4923                } catch (IOException e) {
4924                    Slog.w(TAG, "Couldn't close agent pipes", e);
4925                }
4926                mPipes = null;
4927            }
4928        }
4929
4930        void tearDownAgent(ApplicationInfo app) {
4931            if (mAgent != null) {
4932                try {
4933                    // unbind and tidy up even on timeout or failure, just in case
4934                    mActivityManager.unbindBackupAgent(app);
4935
4936                    // The agent was running with a stub Application object, so shut it down.
4937                    // !!! We hardcode the confirmation UI's package name here rather than use a
4938                    //     manifest flag!  TODO something less direct.
4939                    if (app.uid != Process.SYSTEM_UID
4940                            && !app.packageName.equals("com.android.backupconfirm")) {
4941                        if (DEBUG) Slog.d(TAG, "Killing host process");
4942                        mActivityManager.killApplicationProcess(app.processName, app.uid);
4943                    } else {
4944                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
4945                    }
4946                } catch (RemoteException e) {
4947                    Slog.d(TAG, "Lost app trying to shut down");
4948                }
4949                mAgent = null;
4950            }
4951        }
4952
4953        class RestoreInstallObserver extends PackageInstallObserver {
4954            final AtomicBoolean mDone = new AtomicBoolean();
4955            String mPackageName;
4956            int mResult;
4957
4958            public void reset() {
4959                synchronized (mDone) {
4960                    mDone.set(false);
4961                }
4962            }
4963
4964            public void waitForCompletion() {
4965                synchronized (mDone) {
4966                    while (mDone.get() == false) {
4967                        try {
4968                            mDone.wait();
4969                        } catch (InterruptedException e) { }
4970                    }
4971                }
4972            }
4973
4974            int getResult() {
4975                return mResult;
4976            }
4977
4978            @Override
4979            public void onPackageInstalled(String packageName, int returnCode,
4980                    String msg, Bundle extras) {
4981                synchronized (mDone) {
4982                    mResult = returnCode;
4983                    mPackageName = packageName;
4984                    mDone.set(true);
4985                    mDone.notifyAll();
4986                }
4987            }
4988        }
4989
4990        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
4991            final AtomicBoolean mDone = new AtomicBoolean();
4992            int mResult;
4993
4994            public void reset() {
4995                synchronized (mDone) {
4996                    mDone.set(false);
4997                }
4998            }
4999
5000            public void waitForCompletion() {
5001                synchronized (mDone) {
5002                    while (mDone.get() == false) {
5003                        try {
5004                            mDone.wait();
5005                        } catch (InterruptedException e) { }
5006                    }
5007                }
5008            }
5009
5010            @Override
5011            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
5012                synchronized (mDone) {
5013                    mResult = returnCode;
5014                    mDone.set(true);
5015                    mDone.notifyAll();
5016                }
5017            }
5018        }
5019
5020        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
5021        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
5022
5023        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
5024            boolean okay = true;
5025
5026            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
5027
5028            // The file content is an .apk file.  Copy it out to a staging location and
5029            // attempt to install it.
5030            File apkFile = new File(mDataDir, info.packageName);
5031            try {
5032                FileOutputStream apkStream = new FileOutputStream(apkFile);
5033                byte[] buffer = new byte[32 * 1024];
5034                long size = info.size;
5035                while (size > 0) {
5036                    long toRead = (buffer.length < size) ? buffer.length : size;
5037                    int didRead = instream.read(buffer, 0, (int)toRead);
5038                    if (didRead >= 0) mBytes += didRead;
5039                    apkStream.write(buffer, 0, didRead);
5040                    size -= didRead;
5041                }
5042                apkStream.close();
5043
5044                // make sure the installer can read it
5045                apkFile.setReadable(true, false);
5046
5047                // Now install it
5048                Uri packageUri = Uri.fromFile(apkFile);
5049                mInstallObserver.reset();
5050                mPackageManager.installPackage(packageUri, mInstallObserver,
5051                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
5052                        installerPackage);
5053                mInstallObserver.waitForCompletion();
5054
5055                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
5056                    // The only time we continue to accept install of data even if the
5057                    // apk install failed is if we had already determined that we could
5058                    // accept the data regardless.
5059                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
5060                        okay = false;
5061                    }
5062                } else {
5063                    // Okay, the install succeeded.  Make sure it was the right app.
5064                    boolean uninstall = false;
5065                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
5066                        Slog.w(TAG, "Restore stream claimed to include apk for "
5067                                + info.packageName + " but apk was really "
5068                                + mInstallObserver.mPackageName);
5069                        // delete the package we just put in place; it might be fraudulent
5070                        okay = false;
5071                        uninstall = true;
5072                    } else {
5073                        try {
5074                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
5075                                    PackageManager.GET_SIGNATURES);
5076                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
5077                                Slog.w(TAG, "Restore stream contains apk of package "
5078                                        + info.packageName + " but it disallows backup/restore");
5079                                okay = false;
5080                            } else {
5081                                // So far so good -- do the signatures match the manifest?
5082                                Signature[] sigs = mManifestSignatures.get(info.packageName);
5083                                if (signaturesMatch(sigs, pkg)) {
5084                                    // If this is a system-uid app without a declared backup agent,
5085                                    // don't restore any of the file data.
5086                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
5087                                            && (pkg.applicationInfo.backupAgentName == null)) {
5088                                        Slog.w(TAG, "Installed app " + info.packageName
5089                                                + " has restricted uid and no agent");
5090                                        okay = false;
5091                                    }
5092                                } else {
5093                                    Slog.w(TAG, "Installed app " + info.packageName
5094                                            + " signatures do not match restore manifest");
5095                                    okay = false;
5096                                    uninstall = true;
5097                                }
5098                            }
5099                        } catch (NameNotFoundException e) {
5100                            Slog.w(TAG, "Install of package " + info.packageName
5101                                    + " succeeded but now not found");
5102                            okay = false;
5103                        }
5104                    }
5105
5106                    // If we're not okay at this point, we need to delete the package
5107                    // that we just installed.
5108                    if (uninstall) {
5109                        mDeleteObserver.reset();
5110                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
5111                                mDeleteObserver, 0);
5112                        mDeleteObserver.waitForCompletion();
5113                    }
5114                }
5115            } catch (IOException e) {
5116                Slog.e(TAG, "Unable to transcribe restored apk for install");
5117                okay = false;
5118            } finally {
5119                apkFile.delete();
5120            }
5121
5122            return okay;
5123        }
5124
5125        // Given an actual file content size, consume the post-content padding mandated
5126        // by the tar format.
5127        void skipTarPadding(long size, InputStream instream) throws IOException {
5128            long partial = (size + 512) % 512;
5129            if (partial > 0) {
5130                final int needed = 512 - (int)partial;
5131                if (MORE_DEBUG) {
5132                    Slog.i(TAG, "Skipping tar padding: " + needed + " bytes");
5133                }
5134                byte[] buffer = new byte[needed];
5135                if (readExactly(instream, buffer, 0, needed) == needed) {
5136                    mBytes += needed;
5137                } else throw new IOException("Unexpected EOF in padding");
5138            }
5139        }
5140
5141        // Read a widget metadata file, returning the restored blob
5142        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
5143            // Fail on suspiciously large widget dump files
5144            if (info.size > 64 * 1024) {
5145                throw new IOException("Metadata too big; corrupt? size=" + info.size);
5146            }
5147
5148            byte[] buffer = new byte[(int) info.size];
5149            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5150                mBytes += info.size;
5151            } else throw new IOException("Unexpected EOF in widget data");
5152
5153            String[] str = new String[1];
5154            int offset = extractLine(buffer, 0, str);
5155            int version = Integer.parseInt(str[0]);
5156            if (version == BACKUP_MANIFEST_VERSION) {
5157                offset = extractLine(buffer, offset, str);
5158                final String pkg = str[0];
5159                if (info.packageName.equals(pkg)) {
5160                    // Data checks out -- the rest of the buffer is a concatenation of
5161                    // binary blobs as described in the comment at writeAppWidgetData()
5162                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
5163                            offset, buffer.length - offset);
5164                    DataInputStream in = new DataInputStream(bin);
5165                    while (bin.available() > 0) {
5166                        int token = in.readInt();
5167                        int size = in.readInt();
5168                        if (size > 64 * 1024) {
5169                            throw new IOException("Datum "
5170                                    + Integer.toHexString(token)
5171                                    + " too big; corrupt? size=" + info.size);
5172                        }
5173                        switch (token) {
5174                            case BACKUP_WIDGET_METADATA_TOKEN:
5175                            {
5176                                if (MORE_DEBUG) {
5177                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
5178                                }
5179                                mWidgetData = new byte[size];
5180                                in.read(mWidgetData);
5181                                break;
5182                            }
5183                            default:
5184                            {
5185                                if (DEBUG) {
5186                                    Slog.i(TAG, "Ignoring metadata blob "
5187                                            + Integer.toHexString(token)
5188                                            + " for " + info.packageName);
5189                                }
5190                                in.skipBytes(size);
5191                                break;
5192                            }
5193                        }
5194                    }
5195                } else {
5196                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
5197                            + " but widget data for " + pkg);
5198                }
5199            } else {
5200                Slog.w(TAG, "Unsupported metadata version " + version);
5201            }
5202        }
5203
5204        // Returns a policy constant
5205        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
5206                throws IOException {
5207            // Fail on suspiciously large manifest files
5208            if (info.size > 64 * 1024) {
5209                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
5210            }
5211
5212            byte[] buffer = new byte[(int) info.size];
5213            if (MORE_DEBUG) {
5214                Slog.i(TAG, "   readAppManifest() looking for " + info.size + " bytes, "
5215                        + mBytes + " already consumed");
5216            }
5217            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5218                mBytes += info.size;
5219            } else throw new IOException("Unexpected EOF in manifest");
5220
5221            RestorePolicy policy = RestorePolicy.IGNORE;
5222            String[] str = new String[1];
5223            int offset = 0;
5224
5225            try {
5226                offset = extractLine(buffer, offset, str);
5227                int version = Integer.parseInt(str[0]);
5228                if (version == BACKUP_MANIFEST_VERSION) {
5229                    offset = extractLine(buffer, offset, str);
5230                    String manifestPackage = str[0];
5231                    // TODO: handle <original-package>
5232                    if (manifestPackage.equals(info.packageName)) {
5233                        offset = extractLine(buffer, offset, str);
5234                        version = Integer.parseInt(str[0]);  // app version
5235                        offset = extractLine(buffer, offset, str);
5236                        // This is the platform version, which we don't use, but we parse it
5237                        // as a safety against corruption in the manifest.
5238                        Integer.parseInt(str[0]);
5239                        offset = extractLine(buffer, offset, str);
5240                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
5241                        offset = extractLine(buffer, offset, str);
5242                        boolean hasApk = str[0].equals("1");
5243                        offset = extractLine(buffer, offset, str);
5244                        int numSigs = Integer.parseInt(str[0]);
5245                        if (numSigs > 0) {
5246                            Signature[] sigs = new Signature[numSigs];
5247                            for (int i = 0; i < numSigs; i++) {
5248                                offset = extractLine(buffer, offset, str);
5249                                sigs[i] = new Signature(str[0]);
5250                            }
5251                            mManifestSignatures.put(info.packageName, sigs);
5252
5253                            // Okay, got the manifest info we need...
5254                            try {
5255                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
5256                                        info.packageName, PackageManager.GET_SIGNATURES);
5257                                // Fall through to IGNORE if the app explicitly disallows backup
5258                                final int flags = pkgInfo.applicationInfo.flags;
5259                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
5260                                    // Restore system-uid-space packages only if they have
5261                                    // defined a custom backup agent
5262                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
5263                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
5264                                        // Verify signatures against any installed version; if they
5265                                        // don't match, then we fall though and ignore the data.  The
5266                                        // signatureMatch() method explicitly ignores the signature
5267                                        // check for packages installed on the system partition, because
5268                                        // such packages are signed with the platform cert instead of
5269                                        // the app developer's cert, so they're different on every
5270                                        // device.
5271                                        if (signaturesMatch(sigs, pkgInfo)) {
5272                                            if (pkgInfo.versionCode >= version) {
5273                                                Slog.i(TAG, "Sig + version match; taking data");
5274                                                policy = RestorePolicy.ACCEPT;
5275                                            } else {
5276                                                // The data is from a newer version of the app than
5277                                                // is presently installed.  That means we can only
5278                                                // use it if the matching apk is also supplied.
5279                                                if (mAllowApks) {
5280                                                    Slog.i(TAG, "Data version " + version
5281                                                            + " is newer than installed version "
5282                                                            + pkgInfo.versionCode
5283                                                            + " - requiring apk");
5284                                                    policy = RestorePolicy.ACCEPT_IF_APK;
5285                                                } else {
5286                                                    Slog.i(TAG, "Data requires newer version "
5287                                                            + version + "; ignoring");
5288                                                    policy = RestorePolicy.IGNORE;
5289                                                }
5290                                            }
5291                                        } else {
5292                                            Slog.w(TAG, "Restore manifest signatures do not match "
5293                                                    + "installed application for " + info.packageName);
5294                                        }
5295                                    } else {
5296                                        Slog.w(TAG, "Package " + info.packageName
5297                                                + " is system level with no agent");
5298                                    }
5299                                } else {
5300                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
5301                                            + info.packageName + " but allowBackup=false");
5302                                }
5303                            } catch (NameNotFoundException e) {
5304                                // Okay, the target app isn't installed.  We can process
5305                                // the restore properly only if the dataset provides the
5306                                // apk file and we can successfully install it.
5307                                if (mAllowApks) {
5308                                    if (DEBUG) Slog.i(TAG, "Package " + info.packageName
5309                                            + " not installed; requiring apk in dataset");
5310                                    policy = RestorePolicy.ACCEPT_IF_APK;
5311                                } else {
5312                                    policy = RestorePolicy.IGNORE;
5313                                }
5314                            }
5315
5316                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
5317                                Slog.i(TAG, "Cannot restore package " + info.packageName
5318                                        + " without the matching .apk");
5319                            }
5320                        } else {
5321                            Slog.i(TAG, "Missing signature on backed-up package "
5322                                    + info.packageName);
5323                        }
5324                    } else {
5325                        Slog.i(TAG, "Expected package " + info.packageName
5326                                + " but restore manifest claims " + manifestPackage);
5327                    }
5328                } else {
5329                    Slog.i(TAG, "Unknown restore manifest version " + version
5330                            + " for package " + info.packageName);
5331                }
5332            } catch (NumberFormatException e) {
5333                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
5334            } catch (IllegalArgumentException e) {
5335                Slog.w(TAG, e.getMessage());
5336            }
5337
5338            return policy;
5339        }
5340
5341        // Builds a line from a byte buffer starting at 'offset', and returns
5342        // the index of the next unconsumed data in the buffer.
5343        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
5344            final int end = buffer.length;
5345            if (offset >= end) throw new IOException("Incomplete data");
5346
5347            int pos;
5348            for (pos = offset; pos < end; pos++) {
5349                byte c = buffer[pos];
5350                // at LF we declare end of line, and return the next char as the
5351                // starting point for the next time through
5352                if (c == '\n') {
5353                    break;
5354                }
5355            }
5356            outStr[0] = new String(buffer, offset, pos - offset);
5357            pos++;  // may be pointing an extra byte past the end but that's okay
5358            return pos;
5359        }
5360
5361        void dumpFileMetadata(FileMetadata info) {
5362            if (DEBUG) {
5363                StringBuilder b = new StringBuilder(128);
5364
5365                // mode string
5366                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
5367                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
5368                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
5369                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
5370                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
5371                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
5372                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
5373                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
5374                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
5375                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
5376                b.append(String.format(" %9d ", info.size));
5377
5378                Date stamp = new Date(info.mtime);
5379                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
5380
5381                b.append(info.packageName);
5382                b.append(" :: ");
5383                b.append(info.domain);
5384                b.append(" :: ");
5385                b.append(info.path);
5386
5387                Slog.i(TAG, b.toString());
5388            }
5389        }
5390
5391        // Consume a tar file header block [sequence] and accumulate the relevant metadata
5392        FileMetadata readTarHeaders(InputStream instream) throws IOException {
5393            byte[] block = new byte[512];
5394            FileMetadata info = null;
5395
5396            boolean gotHeader = readTarHeader(instream, block);
5397            if (gotHeader) {
5398                try {
5399                    // okay, presume we're okay, and extract the various metadata
5400                    info = new FileMetadata();
5401                    info.size = extractRadix(block, 124, 12, 8);
5402                    info.mtime = extractRadix(block, 136, 12, 8);
5403                    info.mode = extractRadix(block, 100, 8, 8);
5404
5405                    info.path = extractString(block, 345, 155); // prefix
5406                    String path = extractString(block, 0, 100);
5407                    if (path.length() > 0) {
5408                        if (info.path.length() > 0) info.path += '/';
5409                        info.path += path;
5410                    }
5411
5412                    // tar link indicator field: 1 byte at offset 156 in the header.
5413                    int typeChar = block[156];
5414                    if (typeChar == 'x') {
5415                        // pax extended header, so we need to read that
5416                        gotHeader = readPaxExtendedHeader(instream, info);
5417                        if (gotHeader) {
5418                            // and after a pax extended header comes another real header -- read
5419                            // that to find the real file type
5420                            gotHeader = readTarHeader(instream, block);
5421                        }
5422                        if (!gotHeader) throw new IOException("Bad or missing pax header");
5423
5424                        typeChar = block[156];
5425                    }
5426
5427                    switch (typeChar) {
5428                        case '0': info.type = BackupAgent.TYPE_FILE; break;
5429                        case '5': {
5430                            info.type = BackupAgent.TYPE_DIRECTORY;
5431                            if (info.size != 0) {
5432                                Slog.w(TAG, "Directory entry with nonzero size in header");
5433                                info.size = 0;
5434                            }
5435                            break;
5436                        }
5437                        case 0: {
5438                            // presume EOF
5439                            if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
5440                            return null;
5441                        }
5442                        default: {
5443                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
5444                            throw new IOException("Unknown entity type " + typeChar);
5445                        }
5446                    }
5447
5448                    // Parse out the path
5449                    //
5450                    // first: apps/shared/unrecognized
5451                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
5452                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
5453                        // File in shared storage.  !!! TODO: implement this.
5454                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
5455                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
5456                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
5457                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
5458                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
5459                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
5460                        // App content!  Parse out the package name and domain
5461
5462                        // strip the apps/ prefix
5463                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
5464
5465                        // extract the package name
5466                        int slash = info.path.indexOf('/');
5467                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
5468                        info.packageName = info.path.substring(0, slash);
5469                        info.path = info.path.substring(slash+1);
5470
5471                        // if it's a manifest or metadata payload we're done, otherwise parse
5472                        // out the domain into which the file will be restored
5473                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
5474                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
5475                            slash = info.path.indexOf('/');
5476                            if (slash < 0) {
5477                                throw new IOException("Illegal semantic path in non-manifest "
5478                                        + info.path);
5479                            }
5480                            info.domain = info.path.substring(0, slash);
5481                            info.path = info.path.substring(slash + 1);
5482                        }
5483                    }
5484                } catch (IOException e) {
5485                    if (DEBUG) {
5486                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
5487                        HEXLOG(block);
5488                    }
5489                    throw e;
5490                }
5491            }
5492            return info;
5493        }
5494
5495        private boolean isRestorableFile(FileMetadata info) {
5496            if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
5497                if (MORE_DEBUG) {
5498                    Slog.i(TAG, "Dropping cache file path " + info.path);
5499                }
5500                return false;
5501            }
5502
5503            if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
5504                // It's possible this is "no-backup" dir contents in an archive stream
5505                // produced on a device running a version of the OS that predates that
5506                // API.  Respect the no-backup intention and don't let the data get to
5507                // the app.
5508                if (info.path.startsWith("no_backup/")) {
5509                    if (MORE_DEBUG) {
5510                        Slog.i(TAG, "Dropping no_backup file path " + info.path);
5511                    }
5512                    return false;
5513                }
5514            }
5515
5516            // The path needs to be canonical
5517            if (info.path.contains("..") || info.path.contains("//")) {
5518                if (MORE_DEBUG) {
5519                    Slog.w(TAG, "Dropping invalid path " + info.path);
5520                }
5521                return false;
5522            }
5523
5524            // Otherwise we think this file is good to go
5525            return true;
5526        }
5527
5528        private void HEXLOG(byte[] block) {
5529            int offset = 0;
5530            int todo = block.length;
5531            StringBuilder buf = new StringBuilder(64);
5532            while (todo > 0) {
5533                buf.append(String.format("%04x   ", offset));
5534                int numThisLine = (todo > 16) ? 16 : todo;
5535                for (int i = 0; i < numThisLine; i++) {
5536                    buf.append(String.format("%02x ", block[offset+i]));
5537                }
5538                Slog.i("hexdump", buf.toString());
5539                buf.setLength(0);
5540                todo -= numThisLine;
5541                offset += numThisLine;
5542            }
5543        }
5544
5545        // Read exactly the given number of bytes into a buffer at the stated offset.
5546        // Returns false if EOF is encountered before the requested number of bytes
5547        // could be read.
5548        int readExactly(InputStream in, byte[] buffer, int offset, int size)
5549                throws IOException {
5550            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
5551if (MORE_DEBUG) Slog.i(TAG, "  ... readExactly(" + size + ") called");
5552            int soFar = 0;
5553            while (soFar < size) {
5554                int nRead = in.read(buffer, offset + soFar, size - soFar);
5555                if (nRead <= 0) {
5556                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
5557                    break;
5558                }
5559                soFar += nRead;
5560if (MORE_DEBUG) Slog.v(TAG, "   + got " + nRead + "; now wanting " + (size - soFar));
5561            }
5562            return soFar;
5563        }
5564
5565        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
5566            final int got = readExactly(instream, block, 0, 512);
5567            if (got == 0) return false;     // Clean EOF
5568            if (got < 512) throw new IOException("Unable to read full block header");
5569            mBytes += 512;
5570            return true;
5571        }
5572
5573        // overwrites 'info' fields based on the pax extended header
5574        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
5575                throws IOException {
5576            // We should never see a pax extended header larger than this
5577            if (info.size > 32*1024) {
5578                Slog.w(TAG, "Suspiciously large pax header size " + info.size
5579                        + " - aborting");
5580                throw new IOException("Sanity failure: pax header size " + info.size);
5581            }
5582
5583            // read whole blocks, not just the content size
5584            int numBlocks = (int)((info.size + 511) >> 9);
5585            byte[] data = new byte[numBlocks * 512];
5586            if (readExactly(instream, data, 0, data.length) < data.length) {
5587                throw new IOException("Unable to read full pax header");
5588            }
5589            mBytes += data.length;
5590
5591            final int contentSize = (int) info.size;
5592            int offset = 0;
5593            do {
5594                // extract the line at 'offset'
5595                int eol = offset+1;
5596                while (eol < contentSize && data[eol] != ' ') eol++;
5597                if (eol >= contentSize) {
5598                    // error: we just hit EOD looking for the end of the size field
5599                    throw new IOException("Invalid pax data");
5600                }
5601                // eol points to the space between the count and the key
5602                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
5603                int key = eol + 1;  // start of key=value
5604                eol = offset + linelen - 1; // trailing LF
5605                int value;
5606                for (value = key+1; data[value] != '=' && value <= eol; value++);
5607                if (value > eol) {
5608                    throw new IOException("Invalid pax declaration");
5609                }
5610
5611                // pax requires that key/value strings be in UTF-8
5612                String keyStr = new String(data, key, value-key, "UTF-8");
5613                // -1 to strip the trailing LF
5614                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
5615
5616                if ("path".equals(keyStr)) {
5617                    info.path = valStr;
5618                } else if ("size".equals(keyStr)) {
5619                    info.size = Long.parseLong(valStr);
5620                } else {
5621                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
5622                }
5623
5624                offset += linelen;
5625            } while (offset < contentSize);
5626
5627            return true;
5628        }
5629
5630        long extractRadix(byte[] data, int offset, int maxChars, int radix)
5631                throws IOException {
5632            long value = 0;
5633            final int end = offset + maxChars;
5634            for (int i = offset; i < end; i++) {
5635                final byte b = data[i];
5636                // Numeric fields in tar can terminate with either NUL or SPC
5637                if (b == 0 || b == ' ') break;
5638                if (b < '0' || b > ('0' + radix - 1)) {
5639                    throw new IOException("Invalid number in header: '" + (char)b
5640                            + "' for radix " + radix);
5641                }
5642                value = radix * value + (b - '0');
5643            }
5644            return value;
5645        }
5646
5647        String extractString(byte[] data, int offset, int maxChars) throws IOException {
5648            final int end = offset + maxChars;
5649            int eos = offset;
5650            // tar string fields terminate early with a NUL
5651            while (eos < end && data[eos] != 0) eos++;
5652            return new String(data, offset, eos-offset, "US-ASCII");
5653        }
5654
5655        void sendStartRestore() {
5656            if (mObserver != null) {
5657                try {
5658                    mObserver.onStartRestore();
5659                } catch (RemoteException e) {
5660                    Slog.w(TAG, "full restore observer went away: startRestore");
5661                    mObserver = null;
5662                }
5663            }
5664        }
5665
5666        void sendOnRestorePackage(String name) {
5667            if (mObserver != null) {
5668                try {
5669                    // TODO: use a more user-friendly name string
5670                    mObserver.onRestorePackage(name);
5671                } catch (RemoteException e) {
5672                    Slog.w(TAG, "full restore observer went away: restorePackage");
5673                    mObserver = null;
5674                }
5675            }
5676        }
5677
5678        void sendEndRestore() {
5679            if (mObserver != null) {
5680                try {
5681                    mObserver.onEndRestore();
5682                } catch (RemoteException e) {
5683                    Slog.w(TAG, "full restore observer went away: endRestore");
5684                    mObserver = null;
5685                }
5686            }
5687        }
5688    }
5689
5690    // ***** end new engine class ***
5691
5692    class PerformAdbRestoreTask implements Runnable {
5693        ParcelFileDescriptor mInputFile;
5694        String mCurrentPassword;
5695        String mDecryptPassword;
5696        IFullBackupRestoreObserver mObserver;
5697        AtomicBoolean mLatchObject;
5698        IBackupAgent mAgent;
5699        String mAgentPackage;
5700        ApplicationInfo mTargetApp;
5701        FullBackupObbConnection mObbConnection = null;
5702        ParcelFileDescriptor[] mPipes = null;
5703        byte[] mWidgetData = null;
5704
5705        long mBytes;
5706
5707        // possible handling states for a given package in the restore dataset
5708        final HashMap<String, RestorePolicy> mPackagePolicies
5709                = new HashMap<String, RestorePolicy>();
5710
5711        // installer package names for each encountered app, derived from the manifests
5712        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
5713
5714        // Signatures for a given package found in its manifest file
5715        final HashMap<String, Signature[]> mManifestSignatures
5716                = new HashMap<String, Signature[]>();
5717
5718        // Packages we've already wiped data on when restoring their first file
5719        final HashSet<String> mClearedPackages = new HashSet<String>();
5720
5721        PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
5722                IFullBackupRestoreObserver observer, AtomicBoolean latch) {
5723            mInputFile = fd;
5724            mCurrentPassword = curPassword;
5725            mDecryptPassword = decryptPassword;
5726            mObserver = observer;
5727            mLatchObject = latch;
5728            mAgent = null;
5729            mAgentPackage = null;
5730            mTargetApp = null;
5731            mObbConnection = new FullBackupObbConnection();
5732
5733            // Which packages we've already wiped data on.  We prepopulate this
5734            // with a whitelist of packages known to be unclearable.
5735            mClearedPackages.add("android");
5736            mClearedPackages.add(SETTINGS_PACKAGE);
5737        }
5738
5739        class RestoreFileRunnable implements Runnable {
5740            IBackupAgent mAgent;
5741            FileMetadata mInfo;
5742            ParcelFileDescriptor mSocket;
5743            int mToken;
5744
5745            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
5746                    ParcelFileDescriptor socket, int token) throws IOException {
5747                mAgent = agent;
5748                mInfo = info;
5749                mToken = token;
5750
5751                // This class is used strictly for process-local binder invocations.  The
5752                // semantics of ParcelFileDescriptor differ in this case; in particular, we
5753                // do not automatically get a 'dup'ed descriptor that we can can continue
5754                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
5755                // before proceeding to do the restore.
5756                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
5757            }
5758
5759            @Override
5760            public void run() {
5761                try {
5762                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
5763                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
5764                            mToken, mBackupManagerBinder);
5765                } catch (RemoteException e) {
5766                    // never happens; this is used strictly for local binder calls
5767                }
5768            }
5769        }
5770
5771        @Override
5772        public void run() {
5773            Slog.i(TAG, "--- Performing full-dataset restore ---");
5774            mObbConnection.establish();
5775            sendStartRestore();
5776
5777            // Are we able to restore shared-storage data?
5778            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
5779                mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
5780            }
5781
5782            FileInputStream rawInStream = null;
5783            DataInputStream rawDataIn = null;
5784            try {
5785                if (!backupPasswordMatches(mCurrentPassword)) {
5786                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
5787                    return;
5788                }
5789
5790                mBytes = 0;
5791                byte[] buffer = new byte[32 * 1024];
5792                rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
5793                rawDataIn = new DataInputStream(rawInStream);
5794
5795                // First, parse out the unencrypted/uncompressed header
5796                boolean compressed = false;
5797                InputStream preCompressStream = rawInStream;
5798                final InputStream in;
5799
5800                boolean okay = false;
5801                final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
5802                byte[] streamHeader = new byte[headerLen];
5803                rawDataIn.readFully(streamHeader);
5804                byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
5805                if (Arrays.equals(magicBytes, streamHeader)) {
5806                    // okay, header looks good.  now parse out the rest of the fields.
5807                    String s = readHeaderLine(rawInStream);
5808                    final int archiveVersion = Integer.parseInt(s);
5809                    if (archiveVersion <= BACKUP_FILE_VERSION) {
5810                        // okay, it's a version we recognize.  if it's version 1, we may need
5811                        // to try two different PBKDF2 regimes to compare checksums.
5812                        final boolean pbkdf2Fallback = (archiveVersion == 1);
5813
5814                        s = readHeaderLine(rawInStream);
5815                        compressed = (Integer.parseInt(s) != 0);
5816                        s = readHeaderLine(rawInStream);
5817                        if (s.equals("none")) {
5818                            // no more header to parse; we're good to go
5819                            okay = true;
5820                        } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
5821                            preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
5822                                    rawInStream);
5823                            if (preCompressStream != null) {
5824                                okay = true;
5825                            }
5826                        } else Slog.w(TAG, "Archive is encrypted but no password given");
5827                    } else Slog.w(TAG, "Wrong header version: " + s);
5828                } else Slog.w(TAG, "Didn't read the right header magic");
5829
5830                if (!okay) {
5831                    Slog.w(TAG, "Invalid restore data; aborting.");
5832                    return;
5833                }
5834
5835                // okay, use the right stream layer based on compression
5836                in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
5837
5838                boolean didRestore;
5839                do {
5840                    didRestore = restoreOneFile(in, buffer);
5841                } while (didRestore);
5842
5843                if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
5844            } catch (IOException e) {
5845                Slog.e(TAG, "Unable to read restore input");
5846            } finally {
5847                tearDownPipes();
5848                tearDownAgent(mTargetApp);
5849
5850                try {
5851                    if (rawDataIn != null) rawDataIn.close();
5852                    if (rawInStream != null) rawInStream.close();
5853                    mInputFile.close();
5854                } catch (IOException e) {
5855                    Slog.w(TAG, "Close of restore data pipe threw", e);
5856                    /* nothing we can do about this */
5857                }
5858                synchronized (mCurrentOpLock) {
5859                    mCurrentOperations.clear();
5860                }
5861                synchronized (mLatchObject) {
5862                    mLatchObject.set(true);
5863                    mLatchObject.notifyAll();
5864                }
5865                mObbConnection.tearDown();
5866                sendEndRestore();
5867                Slog.d(TAG, "Full restore pass complete.");
5868                mWakelock.release();
5869            }
5870        }
5871
5872        String readHeaderLine(InputStream in) throws IOException {
5873            int c;
5874            StringBuilder buffer = new StringBuilder(80);
5875            while ((c = in.read()) >= 0) {
5876                if (c == '\n') break;   // consume and discard the newlines
5877                buffer.append((char)c);
5878            }
5879            return buffer.toString();
5880        }
5881
5882        InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
5883                int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
5884                boolean doLog) {
5885            InputStream result = null;
5886
5887            try {
5888                Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
5889                SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt,
5890                        rounds);
5891                byte[] IV = hexToByteArray(userIvHex);
5892                IvParameterSpec ivSpec = new IvParameterSpec(IV);
5893                c.init(Cipher.DECRYPT_MODE,
5894                        new SecretKeySpec(userKey.getEncoded(), "AES"),
5895                        ivSpec);
5896                byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
5897                byte[] mkBlob = c.doFinal(mkCipher);
5898
5899                // first, the master key IV
5900                int offset = 0;
5901                int len = mkBlob[offset++];
5902                IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
5903                offset += len;
5904                // then the master key itself
5905                len = mkBlob[offset++];
5906                byte[] mk = Arrays.copyOfRange(mkBlob,
5907                        offset, offset + len);
5908                offset += len;
5909                // and finally the master key checksum hash
5910                len = mkBlob[offset++];
5911                byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
5912                        offset, offset + len);
5913
5914                // now validate the decrypted master key against the checksum
5915                byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds);
5916                if (Arrays.equals(calculatedCk, mkChecksum)) {
5917                    ivSpec = new IvParameterSpec(IV);
5918                    c.init(Cipher.DECRYPT_MODE,
5919                            new SecretKeySpec(mk, "AES"),
5920                            ivSpec);
5921                    // Only if all of the above worked properly will 'result' be assigned
5922                    result = new CipherInputStream(rawInStream, c);
5923                } else if (doLog) Slog.w(TAG, "Incorrect password");
5924            } catch (InvalidAlgorithmParameterException e) {
5925                if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e);
5926            } catch (BadPaddingException e) {
5927                // This case frequently occurs when the wrong password is used to decrypt
5928                // the master key.  Use the identical "incorrect password" log text as is
5929                // used in the checksum failure log in order to avoid providing additional
5930                // information to an attacker.
5931                if (doLog) Slog.w(TAG, "Incorrect password");
5932            } catch (IllegalBlockSizeException e) {
5933                if (doLog) Slog.w(TAG, "Invalid block size in master key");
5934            } catch (NoSuchAlgorithmException e) {
5935                if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!");
5936            } catch (NoSuchPaddingException e) {
5937                if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!");
5938            } catch (InvalidKeyException e) {
5939                if (doLog) Slog.w(TAG, "Illegal password; aborting");
5940            }
5941
5942            return result;
5943        }
5944
5945        InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
5946                InputStream rawInStream) {
5947            InputStream result = null;
5948            try {
5949                if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
5950
5951                    String userSaltHex = readHeaderLine(rawInStream); // 5
5952                    byte[] userSalt = hexToByteArray(userSaltHex);
5953
5954                    String ckSaltHex = readHeaderLine(rawInStream); // 6
5955                    byte[] ckSalt = hexToByteArray(ckSaltHex);
5956
5957                    int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
5958                    String userIvHex = readHeaderLine(rawInStream); // 8
5959
5960                    String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
5961
5962                    // decrypt the master key blob
5963                    result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt,
5964                            rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
5965                    if (result == null && pbkdf2Fallback) {
5966                        result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt,
5967                                rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
5968                    }
5969                } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
5970            } catch (NumberFormatException e) {
5971                Slog.w(TAG, "Can't parse restore data header");
5972            } catch (IOException e) {
5973                Slog.w(TAG, "Can't read input header");
5974            }
5975
5976            return result;
5977        }
5978
5979        boolean restoreOneFile(InputStream instream, byte[] buffer) {
5980            FileMetadata info;
5981            try {
5982                info = readTarHeaders(instream);
5983                if (info != null) {
5984                    if (MORE_DEBUG) {
5985                        dumpFileMetadata(info);
5986                    }
5987
5988                    final String pkg = info.packageName;
5989                    if (!pkg.equals(mAgentPackage)) {
5990                        // okay, change in package; set up our various
5991                        // bookkeeping if we haven't seen it yet
5992                        if (!mPackagePolicies.containsKey(pkg)) {
5993                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5994                        }
5995
5996                        // Clean up the previous agent relationship if necessary,
5997                        // and let the observer know we're considering a new app.
5998                        if (mAgent != null) {
5999                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
6000                            // Now we're really done
6001                            tearDownPipes();
6002                            tearDownAgent(mTargetApp);
6003                            mTargetApp = null;
6004                            mAgentPackage = null;
6005                        }
6006                    }
6007
6008                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
6009                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
6010                        mPackageInstallers.put(pkg, info.installerPackageName);
6011                        // We've read only the manifest content itself at this point,
6012                        // so consume the footer before looping around to the next
6013                        // input file
6014                        skipTarPadding(info.size, instream);
6015                        sendOnRestorePackage(pkg);
6016                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
6017                        // Metadata blobs!
6018                        readMetadata(info, instream);
6019                        skipTarPadding(info.size, instream);
6020                    } else {
6021                        // Non-manifest, so it's actual file data.  Is this a package
6022                        // we're ignoring?
6023                        boolean okay = true;
6024                        RestorePolicy policy = mPackagePolicies.get(pkg);
6025                        switch (policy) {
6026                            case IGNORE:
6027                                okay = false;
6028                                break;
6029
6030                            case ACCEPT_IF_APK:
6031                                // If we're in accept-if-apk state, then the first file we
6032                                // see MUST be the apk.
6033                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6034                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
6035                                    // Try to install the app.
6036                                    String installerName = mPackageInstallers.get(pkg);
6037                                    okay = installApk(info, installerName, instream);
6038                                    // good to go; promote to ACCEPT
6039                                    mPackagePolicies.put(pkg, (okay)
6040                                            ? RestorePolicy.ACCEPT
6041                                            : RestorePolicy.IGNORE);
6042                                    // At this point we've consumed this file entry
6043                                    // ourselves, so just strip the tar footer and
6044                                    // go on to the next file in the input stream
6045                                    skipTarPadding(info.size, instream);
6046                                    return true;
6047                                } else {
6048                                    // File data before (or without) the apk.  We can't
6049                                    // handle it coherently in this case so ignore it.
6050                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6051                                    okay = false;
6052                                }
6053                                break;
6054
6055                            case ACCEPT:
6056                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6057                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
6058                                    // we can take the data without the apk, so we
6059                                    // *want* to do so.  skip the apk by declaring this
6060                                    // one file not-okay without changing the restore
6061                                    // policy for the package.
6062                                    okay = false;
6063                                }
6064                                break;
6065
6066                            default:
6067                                // Something has gone dreadfully wrong when determining
6068                                // the restore policy from the manifest.  Ignore the
6069                                // rest of this package's data.
6070                                Slog.e(TAG, "Invalid policy from manifest");
6071                                okay = false;
6072                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6073                                break;
6074                        }
6075
6076                        // The path needs to be canonical
6077                        if (info.path.contains("..") || info.path.contains("//")) {
6078                            if (MORE_DEBUG) {
6079                                Slog.w(TAG, "Dropping invalid path " + info.path);
6080                            }
6081                            okay = false;
6082                        }
6083
6084                        // If the policy is satisfied, go ahead and set up to pipe the
6085                        // data to the agent.
6086                        if (DEBUG && okay && mAgent != null) {
6087                            Slog.i(TAG, "Reusing existing agent instance");
6088                        }
6089                        if (okay && mAgent == null) {
6090                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
6091
6092                            try {
6093                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
6094
6095                                // If we haven't sent any data to this app yet, we probably
6096                                // need to clear it first.  Check that.
6097                                if (!mClearedPackages.contains(pkg)) {
6098                                    // apps with their own backup agents are
6099                                    // responsible for coherently managing a full
6100                                    // restore.
6101                                    if (mTargetApp.backupAgentName == null) {
6102                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
6103                                        clearApplicationDataSynchronous(pkg);
6104                                    } else {
6105                                        if (DEBUG) Slog.d(TAG, "backup agent ("
6106                                                + mTargetApp.backupAgentName + ") => no clear");
6107                                    }
6108                                    mClearedPackages.add(pkg);
6109                                } else {
6110                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
6111                                }
6112
6113                                // All set; now set up the IPC and launch the agent
6114                                setUpPipes();
6115                                mAgent = bindToAgentSynchronous(mTargetApp,
6116                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
6117                                mAgentPackage = pkg;
6118                            } catch (IOException e) {
6119                                // fall through to error handling
6120                            } catch (NameNotFoundException e) {
6121                                // fall through to error handling
6122                            }
6123
6124                            if (mAgent == null) {
6125                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
6126                                okay = false;
6127                                tearDownPipes();
6128                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6129                            }
6130                        }
6131
6132                        // Sanity check: make sure we never give data to the wrong app.  This
6133                        // should never happen but a little paranoia here won't go amiss.
6134                        if (okay && !pkg.equals(mAgentPackage)) {
6135                            Slog.e(TAG, "Restoring data for " + pkg
6136                                    + " but agent is for " + mAgentPackage);
6137                            okay = false;
6138                        }
6139
6140                        // At this point we have an agent ready to handle the full
6141                        // restore data as well as a pipe for sending data to
6142                        // that agent.  Tell the agent to start reading from the
6143                        // pipe.
6144                        if (okay) {
6145                            boolean agentSuccess = true;
6146                            long toCopy = info.size;
6147                            final int token = generateToken();
6148                            try {
6149                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
6150                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
6151                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
6152                                            + " : " + info.path);
6153                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
6154                                            info.size, info.type, info.path, info.mode,
6155                                            info.mtime, token, mBackupManagerBinder);
6156                                } else {
6157                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
6158                                            + info.path);
6159                                    // fire up the app's agent listening on the socket.  If
6160                                    // the agent is running in the system process we can't
6161                                    // just invoke it asynchronously, so we provide a thread
6162                                    // for it here.
6163                                    if (mTargetApp.processName.equals("system")) {
6164                                        Slog.d(TAG, "system process agent - spinning a thread");
6165                                        RestoreFileRunnable runner = new RestoreFileRunnable(
6166                                                mAgent, info, mPipes[0], token);
6167                                        new Thread(runner, "restore-sys-runner").start();
6168                                    } else {
6169                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
6170                                                info.domain, info.path, info.mode, info.mtime,
6171                                                token, mBackupManagerBinder);
6172                                    }
6173                                }
6174                            } catch (IOException e) {
6175                                // couldn't dup the socket for a process-local restore
6176                                Slog.d(TAG, "Couldn't establish restore");
6177                                agentSuccess = false;
6178                                okay = false;
6179                            } catch (RemoteException e) {
6180                                // whoops, remote entity went away.  We'll eat the content
6181                                // ourselves, then, and not copy it over.
6182                                Slog.e(TAG, "Agent crashed during full restore");
6183                                agentSuccess = false;
6184                                okay = false;
6185                            }
6186
6187                            // Copy over the data if the agent is still good
6188                            if (okay) {
6189                                boolean pipeOkay = true;
6190                                FileOutputStream pipe = new FileOutputStream(
6191                                        mPipes[1].getFileDescriptor());
6192                                while (toCopy > 0) {
6193                                    int toRead = (toCopy > buffer.length)
6194                                    ? buffer.length : (int)toCopy;
6195                                    int nRead = instream.read(buffer, 0, toRead);
6196                                    if (nRead >= 0) mBytes += nRead;
6197                                    if (nRead <= 0) break;
6198                                    toCopy -= nRead;
6199
6200                                    // send it to the output pipe as long as things
6201                                    // are still good
6202                                    if (pipeOkay) {
6203                                        try {
6204                                            pipe.write(buffer, 0, nRead);
6205                                        } catch (IOException e) {
6206                                            Slog.e(TAG, "Failed to write to restore pipe", e);
6207                                            pipeOkay = false;
6208                                        }
6209                                    }
6210                                }
6211
6212                                // done sending that file!  Now we just need to consume
6213                                // the delta from info.size to the end of block.
6214                                skipTarPadding(info.size, instream);
6215
6216                                // and now that we've sent it all, wait for the remote
6217                                // side to acknowledge receipt
6218                                agentSuccess = waitUntilOperationComplete(token);
6219                            }
6220
6221                            // okay, if the remote end failed at any point, deal with
6222                            // it by ignoring the rest of the restore on it
6223                            if (!agentSuccess) {
6224                                mBackupHandler.removeMessages(MSG_TIMEOUT);
6225                                tearDownPipes();
6226                                tearDownAgent(mTargetApp);
6227                                mAgent = null;
6228                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6229                            }
6230                        }
6231
6232                        // Problems setting up the agent communication, or an already-
6233                        // ignored package: skip to the next tar stream entry by
6234                        // reading and discarding this file.
6235                        if (!okay) {
6236                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
6237                            long bytesToConsume = (info.size + 511) & ~511;
6238                            while (bytesToConsume > 0) {
6239                                int toRead = (bytesToConsume > buffer.length)
6240                                ? buffer.length : (int)bytesToConsume;
6241                                long nRead = instream.read(buffer, 0, toRead);
6242                                if (nRead >= 0) mBytes += nRead;
6243                                if (nRead <= 0) break;
6244                                bytesToConsume -= nRead;
6245                            }
6246                        }
6247                    }
6248                }
6249            } catch (IOException e) {
6250                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
6251                // treat as EOF
6252                info = null;
6253            }
6254
6255            return (info != null);
6256        }
6257
6258        void setUpPipes() throws IOException {
6259            mPipes = ParcelFileDescriptor.createPipe();
6260        }
6261
6262        void tearDownPipes() {
6263            if (mPipes != null) {
6264                try {
6265                    mPipes[0].close();
6266                    mPipes[0] = null;
6267                    mPipes[1].close();
6268                    mPipes[1] = null;
6269                } catch (IOException e) {
6270                    Slog.w(TAG, "Couldn't close agent pipes", e);
6271                }
6272                mPipes = null;
6273            }
6274        }
6275
6276        void tearDownAgent(ApplicationInfo app) {
6277            if (mAgent != null) {
6278                try {
6279                    // unbind and tidy up even on timeout or failure, just in case
6280                    mActivityManager.unbindBackupAgent(app);
6281
6282                    // The agent was running with a stub Application object, so shut it down.
6283                    // !!! We hardcode the confirmation UI's package name here rather than use a
6284                    //     manifest flag!  TODO something less direct.
6285                    if (app.uid != Process.SYSTEM_UID
6286                            && !app.packageName.equals("com.android.backupconfirm")) {
6287                        if (DEBUG) Slog.d(TAG, "Killing host process");
6288                        mActivityManager.killApplicationProcess(app.processName, app.uid);
6289                    } else {
6290                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
6291                    }
6292                } catch (RemoteException e) {
6293                    Slog.d(TAG, "Lost app trying to shut down");
6294                }
6295                mAgent = null;
6296            }
6297        }
6298
6299        class RestoreInstallObserver extends PackageInstallObserver {
6300            final AtomicBoolean mDone = new AtomicBoolean();
6301            String mPackageName;
6302            int mResult;
6303
6304            public void reset() {
6305                synchronized (mDone) {
6306                    mDone.set(false);
6307                }
6308            }
6309
6310            public void waitForCompletion() {
6311                synchronized (mDone) {
6312                    while (mDone.get() == false) {
6313                        try {
6314                            mDone.wait();
6315                        } catch (InterruptedException e) { }
6316                    }
6317                }
6318            }
6319
6320            int getResult() {
6321                return mResult;
6322            }
6323
6324            @Override
6325            public void onPackageInstalled(String packageName, int returnCode,
6326                    String msg, Bundle extras) {
6327                synchronized (mDone) {
6328                    mResult = returnCode;
6329                    mPackageName = packageName;
6330                    mDone.set(true);
6331                    mDone.notifyAll();
6332                }
6333            }
6334        }
6335
6336        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
6337            final AtomicBoolean mDone = new AtomicBoolean();
6338            int mResult;
6339
6340            public void reset() {
6341                synchronized (mDone) {
6342                    mDone.set(false);
6343                }
6344            }
6345
6346            public void waitForCompletion() {
6347                synchronized (mDone) {
6348                    while (mDone.get() == false) {
6349                        try {
6350                            mDone.wait();
6351                        } catch (InterruptedException e) { }
6352                    }
6353                }
6354            }
6355
6356            @Override
6357            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
6358                synchronized (mDone) {
6359                    mResult = returnCode;
6360                    mDone.set(true);
6361                    mDone.notifyAll();
6362                }
6363            }
6364        }
6365
6366        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
6367        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
6368
6369        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
6370            boolean okay = true;
6371
6372            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
6373
6374            // The file content is an .apk file.  Copy it out to a staging location and
6375            // attempt to install it.
6376            File apkFile = new File(mDataDir, info.packageName);
6377            try {
6378                FileOutputStream apkStream = new FileOutputStream(apkFile);
6379                byte[] buffer = new byte[32 * 1024];
6380                long size = info.size;
6381                while (size > 0) {
6382                    long toRead = (buffer.length < size) ? buffer.length : size;
6383                    int didRead = instream.read(buffer, 0, (int)toRead);
6384                    if (didRead >= 0) mBytes += didRead;
6385                    apkStream.write(buffer, 0, didRead);
6386                    size -= didRead;
6387                }
6388                apkStream.close();
6389
6390                // make sure the installer can read it
6391                apkFile.setReadable(true, false);
6392
6393                // Now install it
6394                Uri packageUri = Uri.fromFile(apkFile);
6395                mInstallObserver.reset();
6396                mPackageManager.installPackage(packageUri, mInstallObserver,
6397                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
6398                        installerPackage);
6399                mInstallObserver.waitForCompletion();
6400
6401                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
6402                    // The only time we continue to accept install of data even if the
6403                    // apk install failed is if we had already determined that we could
6404                    // accept the data regardless.
6405                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
6406                        okay = false;
6407                    }
6408                } else {
6409                    // Okay, the install succeeded.  Make sure it was the right app.
6410                    boolean uninstall = false;
6411                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
6412                        Slog.w(TAG, "Restore stream claimed to include apk for "
6413                                + info.packageName + " but apk was really "
6414                                + mInstallObserver.mPackageName);
6415                        // delete the package we just put in place; it might be fraudulent
6416                        okay = false;
6417                        uninstall = true;
6418                    } else {
6419                        try {
6420                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
6421                                    PackageManager.GET_SIGNATURES);
6422                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
6423                                Slog.w(TAG, "Restore stream contains apk of package "
6424                                        + info.packageName + " but it disallows backup/restore");
6425                                okay = false;
6426                            } else {
6427                                // So far so good -- do the signatures match the manifest?
6428                                Signature[] sigs = mManifestSignatures.get(info.packageName);
6429                                if (signaturesMatch(sigs, pkg)) {
6430                                    // If this is a system-uid app without a declared backup agent,
6431                                    // don't restore any of the file data.
6432                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
6433                                            && (pkg.applicationInfo.backupAgentName == null)) {
6434                                        Slog.w(TAG, "Installed app " + info.packageName
6435                                                + " has restricted uid and no agent");
6436                                        okay = false;
6437                                    }
6438                                } else {
6439                                    Slog.w(TAG, "Installed app " + info.packageName
6440                                            + " signatures do not match restore manifest");
6441                                    okay = false;
6442                                    uninstall = true;
6443                                }
6444                            }
6445                        } catch (NameNotFoundException e) {
6446                            Slog.w(TAG, "Install of package " + info.packageName
6447                                    + " succeeded but now not found");
6448                            okay = false;
6449                        }
6450                    }
6451
6452                    // If we're not okay at this point, we need to delete the package
6453                    // that we just installed.
6454                    if (uninstall) {
6455                        mDeleteObserver.reset();
6456                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
6457                                mDeleteObserver, 0);
6458                        mDeleteObserver.waitForCompletion();
6459                    }
6460                }
6461            } catch (IOException e) {
6462                Slog.e(TAG, "Unable to transcribe restored apk for install");
6463                okay = false;
6464            } finally {
6465                apkFile.delete();
6466            }
6467
6468            return okay;
6469        }
6470
6471        // Given an actual file content size, consume the post-content padding mandated
6472        // by the tar format.
6473        void skipTarPadding(long size, InputStream instream) throws IOException {
6474            long partial = (size + 512) % 512;
6475            if (partial > 0) {
6476                final int needed = 512 - (int)partial;
6477                byte[] buffer = new byte[needed];
6478                if (readExactly(instream, buffer, 0, needed) == needed) {
6479                    mBytes += needed;
6480                } else throw new IOException("Unexpected EOF in padding");
6481            }
6482        }
6483
6484        // Read a widget metadata file, returning the restored blob
6485        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
6486            // Fail on suspiciously large widget dump files
6487            if (info.size > 64 * 1024) {
6488                throw new IOException("Metadata too big; corrupt? size=" + info.size);
6489            }
6490
6491            byte[] buffer = new byte[(int) info.size];
6492            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6493                mBytes += info.size;
6494            } else throw new IOException("Unexpected EOF in widget data");
6495
6496            String[] str = new String[1];
6497            int offset = extractLine(buffer, 0, str);
6498            int version = Integer.parseInt(str[0]);
6499            if (version == BACKUP_MANIFEST_VERSION) {
6500                offset = extractLine(buffer, offset, str);
6501                final String pkg = str[0];
6502                if (info.packageName.equals(pkg)) {
6503                    // Data checks out -- the rest of the buffer is a concatenation of
6504                    // binary blobs as described in the comment at writeAppWidgetData()
6505                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
6506                            offset, buffer.length - offset);
6507                    DataInputStream in = new DataInputStream(bin);
6508                    while (bin.available() > 0) {
6509                        int token = in.readInt();
6510                        int size = in.readInt();
6511                        if (size > 64 * 1024) {
6512                            throw new IOException("Datum "
6513                                    + Integer.toHexString(token)
6514                                    + " too big; corrupt? size=" + info.size);
6515                        }
6516                        switch (token) {
6517                            case BACKUP_WIDGET_METADATA_TOKEN:
6518                            {
6519                                if (MORE_DEBUG) {
6520                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
6521                                }
6522                                mWidgetData = new byte[size];
6523                                in.read(mWidgetData);
6524                                break;
6525                            }
6526                            default:
6527                            {
6528                                if (DEBUG) {
6529                                    Slog.i(TAG, "Ignoring metadata blob "
6530                                            + Integer.toHexString(token)
6531                                            + " for " + info.packageName);
6532                                }
6533                                in.skipBytes(size);
6534                                break;
6535                            }
6536                        }
6537                    }
6538                } else {
6539                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
6540                            + " but widget data for " + pkg);
6541                }
6542            } else {
6543                Slog.w(TAG, "Unsupported metadata version " + version);
6544            }
6545        }
6546
6547        // Returns a policy constant; takes a buffer arg to reduce memory churn
6548        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
6549                throws IOException {
6550            // Fail on suspiciously large manifest files
6551            if (info.size > 64 * 1024) {
6552                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
6553            }
6554
6555            byte[] buffer = new byte[(int) info.size];
6556            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6557                mBytes += info.size;
6558            } else throw new IOException("Unexpected EOF in manifest");
6559
6560            RestorePolicy policy = RestorePolicy.IGNORE;
6561            String[] str = new String[1];
6562            int offset = 0;
6563
6564            try {
6565                offset = extractLine(buffer, offset, str);
6566                int version = Integer.parseInt(str[0]);
6567                if (version == BACKUP_MANIFEST_VERSION) {
6568                    offset = extractLine(buffer, offset, str);
6569                    String manifestPackage = str[0];
6570                    // TODO: handle <original-package>
6571                    if (manifestPackage.equals(info.packageName)) {
6572                        offset = extractLine(buffer, offset, str);
6573                        version = Integer.parseInt(str[0]);  // app version
6574                        offset = extractLine(buffer, offset, str);
6575                        // This is the platform version, which we don't use, but we parse it
6576                        // as a safety against corruption in the manifest.
6577                        Integer.parseInt(str[0]);
6578                        offset = extractLine(buffer, offset, str);
6579                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
6580                        offset = extractLine(buffer, offset, str);
6581                        boolean hasApk = str[0].equals("1");
6582                        offset = extractLine(buffer, offset, str);
6583                        int numSigs = Integer.parseInt(str[0]);
6584                        if (numSigs > 0) {
6585                            Signature[] sigs = new Signature[numSigs];
6586                            for (int i = 0; i < numSigs; i++) {
6587                                offset = extractLine(buffer, offset, str);
6588                                sigs[i] = new Signature(str[0]);
6589                            }
6590                            mManifestSignatures.put(info.packageName, sigs);
6591
6592                            // Okay, got the manifest info we need...
6593                            try {
6594                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
6595                                        info.packageName, PackageManager.GET_SIGNATURES);
6596                                // Fall through to IGNORE if the app explicitly disallows backup
6597                                final int flags = pkgInfo.applicationInfo.flags;
6598                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
6599                                    // Restore system-uid-space packages only if they have
6600                                    // defined a custom backup agent
6601                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
6602                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
6603                                        // Verify signatures against any installed version; if they
6604                                        // don't match, then we fall though and ignore the data.  The
6605                                        // signatureMatch() method explicitly ignores the signature
6606                                        // check for packages installed on the system partition, because
6607                                        // such packages are signed with the platform cert instead of
6608                                        // the app developer's cert, so they're different on every
6609                                        // device.
6610                                        if (signaturesMatch(sigs, pkgInfo)) {
6611                                            if (pkgInfo.versionCode >= version) {
6612                                                Slog.i(TAG, "Sig + version match; taking data");
6613                                                policy = RestorePolicy.ACCEPT;
6614                                            } else {
6615                                                // The data is from a newer version of the app than
6616                                                // is presently installed.  That means we can only
6617                                                // use it if the matching apk is also supplied.
6618                                                Slog.d(TAG, "Data version " + version
6619                                                        + " is newer than installed version "
6620                                                        + pkgInfo.versionCode + " - requiring apk");
6621                                                policy = RestorePolicy.ACCEPT_IF_APK;
6622                                            }
6623                                        } else {
6624                                            Slog.w(TAG, "Restore manifest signatures do not match "
6625                                                    + "installed application for " + info.packageName);
6626                                        }
6627                                    } else {
6628                                        Slog.w(TAG, "Package " + info.packageName
6629                                                + " is system level with no agent");
6630                                    }
6631                                } else {
6632                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
6633                                            + info.packageName + " but allowBackup=false");
6634                                }
6635                            } catch (NameNotFoundException e) {
6636                                // Okay, the target app isn't installed.  We can process
6637                                // the restore properly only if the dataset provides the
6638                                // apk file and we can successfully install it.
6639                                if (DEBUG) Slog.i(TAG, "Package " + info.packageName
6640                                        + " not installed; requiring apk in dataset");
6641                                policy = RestorePolicy.ACCEPT_IF_APK;
6642                            }
6643
6644                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
6645                                Slog.i(TAG, "Cannot restore package " + info.packageName
6646                                        + " without the matching .apk");
6647                            }
6648                        } else {
6649                            Slog.i(TAG, "Missing signature on backed-up package "
6650                                    + info.packageName);
6651                        }
6652                    } else {
6653                        Slog.i(TAG, "Expected package " + info.packageName
6654                                + " but restore manifest claims " + manifestPackage);
6655                    }
6656                } else {
6657                    Slog.i(TAG, "Unknown restore manifest version " + version
6658                            + " for package " + info.packageName);
6659                }
6660            } catch (NumberFormatException e) {
6661                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
6662            } catch (IllegalArgumentException e) {
6663                Slog.w(TAG, e.getMessage());
6664            }
6665
6666            return policy;
6667        }
6668
6669        // Builds a line from a byte buffer starting at 'offset', and returns
6670        // the index of the next unconsumed data in the buffer.
6671        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
6672            final int end = buffer.length;
6673            if (offset >= end) throw new IOException("Incomplete data");
6674
6675            int pos;
6676            for (pos = offset; pos < end; pos++) {
6677                byte c = buffer[pos];
6678                // at LF we declare end of line, and return the next char as the
6679                // starting point for the next time through
6680                if (c == '\n') {
6681                    break;
6682                }
6683            }
6684            outStr[0] = new String(buffer, offset, pos - offset);
6685            pos++;  // may be pointing an extra byte past the end but that's okay
6686            return pos;
6687        }
6688
6689        void dumpFileMetadata(FileMetadata info) {
6690            if (DEBUG) {
6691                StringBuilder b = new StringBuilder(128);
6692
6693                // mode string
6694                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
6695                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
6696                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
6697                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
6698                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
6699                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
6700                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
6701                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
6702                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
6703                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
6704                b.append(String.format(" %9d ", info.size));
6705
6706                Date stamp = new Date(info.mtime);
6707                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
6708
6709                b.append(info.packageName);
6710                b.append(" :: ");
6711                b.append(info.domain);
6712                b.append(" :: ");
6713                b.append(info.path);
6714
6715                Slog.i(TAG, b.toString());
6716            }
6717        }
6718        // Consume a tar file header block [sequence] and accumulate the relevant metadata
6719        FileMetadata readTarHeaders(InputStream instream) throws IOException {
6720            byte[] block = new byte[512];
6721            FileMetadata info = null;
6722
6723            boolean gotHeader = readTarHeader(instream, block);
6724            if (gotHeader) {
6725                try {
6726                    // okay, presume we're okay, and extract the various metadata
6727                    info = new FileMetadata();
6728                    info.size = extractRadix(block, 124, 12, 8);
6729                    info.mtime = extractRadix(block, 136, 12, 8);
6730                    info.mode = extractRadix(block, 100, 8, 8);
6731
6732                    info.path = extractString(block, 345, 155); // prefix
6733                    String path = extractString(block, 0, 100);
6734                    if (path.length() > 0) {
6735                        if (info.path.length() > 0) info.path += '/';
6736                        info.path += path;
6737                    }
6738
6739                    // tar link indicator field: 1 byte at offset 156 in the header.
6740                    int typeChar = block[156];
6741                    if (typeChar == 'x') {
6742                        // pax extended header, so we need to read that
6743                        gotHeader = readPaxExtendedHeader(instream, info);
6744                        if (gotHeader) {
6745                            // and after a pax extended header comes another real header -- read
6746                            // that to find the real file type
6747                            gotHeader = readTarHeader(instream, block);
6748                        }
6749                        if (!gotHeader) throw new IOException("Bad or missing pax header");
6750
6751                        typeChar = block[156];
6752                    }
6753
6754                    switch (typeChar) {
6755                        case '0': info.type = BackupAgent.TYPE_FILE; break;
6756                        case '5': {
6757                            info.type = BackupAgent.TYPE_DIRECTORY;
6758                            if (info.size != 0) {
6759                                Slog.w(TAG, "Directory entry with nonzero size in header");
6760                                info.size = 0;
6761                            }
6762                            break;
6763                        }
6764                        case 0: {
6765                            // presume EOF
6766                            if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
6767                            return null;
6768                        }
6769                        default: {
6770                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
6771                            throw new IOException("Unknown entity type " + typeChar);
6772                        }
6773                    }
6774
6775                    // Parse out the path
6776                    //
6777                    // first: apps/shared/unrecognized
6778                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
6779                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
6780                        // File in shared storage.  !!! TODO: implement this.
6781                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
6782                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
6783                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
6784                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
6785                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
6786                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
6787                        // App content!  Parse out the package name and domain
6788
6789                        // strip the apps/ prefix
6790                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
6791
6792                        // extract the package name
6793                        int slash = info.path.indexOf('/');
6794                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
6795                        info.packageName = info.path.substring(0, slash);
6796                        info.path = info.path.substring(slash+1);
6797
6798                        // if it's a manifest or metadata payload we're done, otherwise parse
6799                        // out the domain into which the file will be restored
6800                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
6801                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
6802                            slash = info.path.indexOf('/');
6803                            if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
6804                            info.domain = info.path.substring(0, slash);
6805                            info.path = info.path.substring(slash + 1);
6806                        }
6807                    }
6808                } catch (IOException e) {
6809                    if (DEBUG) {
6810                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
6811                        HEXLOG(block);
6812                    }
6813                    throw e;
6814                }
6815            }
6816            return info;
6817        }
6818
6819        private void HEXLOG(byte[] block) {
6820            int offset = 0;
6821            int todo = block.length;
6822            StringBuilder buf = new StringBuilder(64);
6823            while (todo > 0) {
6824                buf.append(String.format("%04x   ", offset));
6825                int numThisLine = (todo > 16) ? 16 : todo;
6826                for (int i = 0; i < numThisLine; i++) {
6827                    buf.append(String.format("%02x ", block[offset+i]));
6828                }
6829                Slog.i("hexdump", buf.toString());
6830                buf.setLength(0);
6831                todo -= numThisLine;
6832                offset += numThisLine;
6833            }
6834        }
6835
6836        // Read exactly the given number of bytes into a buffer at the stated offset.
6837        // Returns false if EOF is encountered before the requested number of bytes
6838        // could be read.
6839        int readExactly(InputStream in, byte[] buffer, int offset, int size)
6840                throws IOException {
6841            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
6842
6843            int soFar = 0;
6844            while (soFar < size) {
6845                int nRead = in.read(buffer, offset + soFar, size - soFar);
6846                if (nRead <= 0) {
6847                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
6848                    break;
6849                }
6850                soFar += nRead;
6851            }
6852            return soFar;
6853        }
6854
6855        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
6856            final int got = readExactly(instream, block, 0, 512);
6857            if (got == 0) return false;     // Clean EOF
6858            if (got < 512) throw new IOException("Unable to read full block header");
6859            mBytes += 512;
6860            return true;
6861        }
6862
6863        // overwrites 'info' fields based on the pax extended header
6864        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
6865                throws IOException {
6866            // We should never see a pax extended header larger than this
6867            if (info.size > 32*1024) {
6868                Slog.w(TAG, "Suspiciously large pax header size " + info.size
6869                        + " - aborting");
6870                throw new IOException("Sanity failure: pax header size " + info.size);
6871            }
6872
6873            // read whole blocks, not just the content size
6874            int numBlocks = (int)((info.size + 511) >> 9);
6875            byte[] data = new byte[numBlocks * 512];
6876            if (readExactly(instream, data, 0, data.length) < data.length) {
6877                throw new IOException("Unable to read full pax header");
6878            }
6879            mBytes += data.length;
6880
6881            final int contentSize = (int) info.size;
6882            int offset = 0;
6883            do {
6884                // extract the line at 'offset'
6885                int eol = offset+1;
6886                while (eol < contentSize && data[eol] != ' ') eol++;
6887                if (eol >= contentSize) {
6888                    // error: we just hit EOD looking for the end of the size field
6889                    throw new IOException("Invalid pax data");
6890                }
6891                // eol points to the space between the count and the key
6892                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
6893                int key = eol + 1;  // start of key=value
6894                eol = offset + linelen - 1; // trailing LF
6895                int value;
6896                for (value = key+1; data[value] != '=' && value <= eol; value++);
6897                if (value > eol) {
6898                    throw new IOException("Invalid pax declaration");
6899                }
6900
6901                // pax requires that key/value strings be in UTF-8
6902                String keyStr = new String(data, key, value-key, "UTF-8");
6903                // -1 to strip the trailing LF
6904                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
6905
6906                if ("path".equals(keyStr)) {
6907                    info.path = valStr;
6908                } else if ("size".equals(keyStr)) {
6909                    info.size = Long.parseLong(valStr);
6910                } else {
6911                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
6912                }
6913
6914                offset += linelen;
6915            } while (offset < contentSize);
6916
6917            return true;
6918        }
6919
6920        long extractRadix(byte[] data, int offset, int maxChars, int radix)
6921                throws IOException {
6922            long value = 0;
6923            final int end = offset + maxChars;
6924            for (int i = offset; i < end; i++) {
6925                final byte b = data[i];
6926                // Numeric fields in tar can terminate with either NUL or SPC
6927                if (b == 0 || b == ' ') break;
6928                if (b < '0' || b > ('0' + radix - 1)) {
6929                    throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix);
6930                }
6931                value = radix * value + (b - '0');
6932            }
6933            return value;
6934        }
6935
6936        String extractString(byte[] data, int offset, int maxChars) throws IOException {
6937            final int end = offset + maxChars;
6938            int eos = offset;
6939            // tar string fields terminate early with a NUL
6940            while (eos < end && data[eos] != 0) eos++;
6941            return new String(data, offset, eos-offset, "US-ASCII");
6942        }
6943
6944        void sendStartRestore() {
6945            if (mObserver != null) {
6946                try {
6947                    mObserver.onStartRestore();
6948                } catch (RemoteException e) {
6949                    Slog.w(TAG, "full restore observer went away: startRestore");
6950                    mObserver = null;
6951                }
6952            }
6953        }
6954
6955        void sendOnRestorePackage(String name) {
6956            if (mObserver != null) {
6957                try {
6958                    // TODO: use a more user-friendly name string
6959                    mObserver.onRestorePackage(name);
6960                } catch (RemoteException e) {
6961                    Slog.w(TAG, "full restore observer went away: restorePackage");
6962                    mObserver = null;
6963                }
6964            }
6965        }
6966
6967        void sendEndRestore() {
6968            if (mObserver != null) {
6969                try {
6970                    mObserver.onEndRestore();
6971                } catch (RemoteException e) {
6972                    Slog.w(TAG, "full restore observer went away: endRestore");
6973                    mObserver = null;
6974                }
6975            }
6976        }
6977    }
6978
6979    // ----- Restore handling -----
6980
6981    // new style: we only store the SHA-1 hashes of each sig, not the full block
6982    static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
6983        if (target == null) {
6984            return false;
6985        }
6986
6987        // If the target resides on the system partition, we allow it to restore
6988        // data from the like-named package in a restore set even if the signatures
6989        // do not match.  (Unlike general applications, those flashed to the system
6990        // partition will be signed with the device's platform certificate, so on
6991        // different phones the same system app will have different signatures.)
6992        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
6993            if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
6994            return true;
6995        }
6996
6997        // Allow unsigned apps, but not signed on one device and unsigned on the other
6998        // !!! TODO: is this the right policy?
6999        Signature[] deviceSigs = target.signatures;
7000        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
7001                + " device=" + deviceSigs);
7002        if ((storedSigHashes == null || storedSigHashes.size() == 0)
7003                && (deviceSigs == null || deviceSigs.length == 0)) {
7004            return true;
7005        }
7006        if (storedSigHashes == null || deviceSigs == null) {
7007            return false;
7008        }
7009
7010        // !!! TODO: this demands that every stored signature match one
7011        // that is present on device, and does not demand the converse.
7012        // Is this this right policy?
7013        final int nStored = storedSigHashes.size();
7014        final int nDevice = deviceSigs.length;
7015
7016        // hash each on-device signature
7017        ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
7018        for (int i = 0; i < nDevice; i++) {
7019            deviceHashes.add(hashSignature(deviceSigs[i]));
7020        }
7021
7022        // now ensure that each stored sig (hash) matches an on-device sig (hash)
7023        for (int n = 0; n < nStored; n++) {
7024            boolean match = false;
7025            final byte[] storedHash = storedSigHashes.get(n);
7026            for (int i = 0; i < nDevice; i++) {
7027                if (Arrays.equals(storedHash, deviceHashes.get(i))) {
7028                    match = true;
7029                    break;
7030                }
7031            }
7032            // match is false when no on-device sig matched one of the stored ones
7033            if (!match) {
7034                return false;
7035            }
7036        }
7037
7038        return true;
7039    }
7040
7041    static byte[] hashSignature(Signature sig) {
7042        try {
7043            MessageDigest digest = MessageDigest.getInstance("SHA-256");
7044            digest.update(sig.toByteArray());
7045            return digest.digest();
7046        } catch (NoSuchAlgorithmException e) {
7047            Slog.w(TAG, "No SHA-256 algorithm found!");
7048        }
7049        return null;
7050    }
7051
7052    // Old style: directly match the stored vs on device signature blocks
7053    static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
7054        if (target == null) {
7055            return false;
7056        }
7057
7058        // If the target resides on the system partition, we allow it to restore
7059        // data from the like-named package in a restore set even if the signatures
7060        // do not match.  (Unlike general applications, those flashed to the system
7061        // partition will be signed with the device's platform certificate, so on
7062        // different phones the same system app will have different signatures.)
7063        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
7064            if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
7065            return true;
7066        }
7067
7068        // Allow unsigned apps, but not signed on one device and unsigned on the other
7069        // !!! TODO: is this the right policy?
7070        Signature[] deviceSigs = target.signatures;
7071        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
7072                + " device=" + deviceSigs);
7073        if ((storedSigs == null || storedSigs.length == 0)
7074                && (deviceSigs == null || deviceSigs.length == 0)) {
7075            return true;
7076        }
7077        if (storedSigs == null || deviceSigs == null) {
7078            return false;
7079        }
7080
7081        // !!! TODO: this demands that every stored signature match one
7082        // that is present on device, and does not demand the converse.
7083        // Is this this right policy?
7084        int nStored = storedSigs.length;
7085        int nDevice = deviceSigs.length;
7086
7087        for (int i=0; i < nStored; i++) {
7088            boolean match = false;
7089            for (int j=0; j < nDevice; j++) {
7090                if (storedSigs[i].equals(deviceSigs[j])) {
7091                    match = true;
7092                    break;
7093                }
7094            }
7095            if (!match) {
7096                return false;
7097            }
7098        }
7099        return true;
7100    }
7101
7102    // Used by both incremental and full restore
7103    void restoreWidgetData(String packageName, byte[] widgetData) {
7104        // Apply the restored widget state and generate the ID update for the app
7105        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER);
7106    }
7107
7108    // *****************************
7109    // NEW UNIFIED RESTORE IMPLEMENTATION
7110    // *****************************
7111
7112    // states of the unified-restore state machine
7113    enum UnifiedRestoreState {
7114        INITIAL,
7115        RUNNING_QUEUE,
7116        RESTORE_KEYVALUE,
7117        RESTORE_FULL,
7118        RESTORE_FINISHED,
7119        FINAL
7120    }
7121
7122    class PerformUnifiedRestoreTask implements BackupRestoreTask {
7123        // Transport we're working with to do the restore
7124        private IBackupTransport mTransport;
7125
7126        // Where per-transport saved state goes
7127        File mStateDir;
7128
7129        // Restore observer; may be null
7130        private IRestoreObserver mObserver;
7131
7132        // Token identifying the dataset to the transport
7133        private long mToken;
7134
7135        // When this is a restore-during-install, this is the token identifying the
7136        // operation to the Package Manager, and we must ensure that we let it know
7137        // when we're finished.
7138        private int mPmToken;
7139
7140        // Is this a whole-system restore, i.e. are we establishing a new ancestral
7141        // dataset to base future restore-at-install operations from?
7142        private boolean mIsSystemRestore;
7143
7144        // If this is a single-package restore, what package are we interested in?
7145        private PackageInfo mTargetPackage;
7146
7147        // In all cases, the calculated list of packages that we are trying to restore
7148        private List<PackageInfo> mAcceptSet;
7149
7150        // Our bookkeeping about the ancestral dataset
7151        private PackageManagerBackupAgent mPmAgent;
7152
7153        // Currently-bound backup agent for restore + restoreFinished purposes
7154        private IBackupAgent mAgent;
7155
7156        // What sort of restore we're doing now
7157        private RestoreDescription mRestoreDescription;
7158
7159        // The package we're currently restoring
7160        private PackageInfo mCurrentPackage;
7161
7162        // Widget-related data handled as part of this restore operation
7163        private byte[] mWidgetData;
7164
7165        // Number of apps restored in this pass
7166        private int mCount;
7167
7168        // When did we start?
7169        private long mStartRealtime;
7170
7171        // State machine progress
7172        private UnifiedRestoreState mState;
7173
7174        // How are things going?
7175        private int mStatus;
7176
7177        // Done?
7178        private boolean mFinished;
7179
7180        // Key/value: bookkeeping about staged data and files for agent access
7181        private File mBackupDataName;
7182        private File mStageName;
7183        private File mSavedStateName;
7184        private File mNewStateName;
7185        ParcelFileDescriptor mBackupData;
7186        ParcelFileDescriptor mNewState;
7187
7188        // Invariant: mWakelock is already held, and this task is responsible for
7189        // releasing it at the end of the restore operation.
7190        PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer,
7191                long restoreSetToken, PackageInfo targetPackage, int pmToken,
7192                boolean isFullSystemRestore, String[] filterSet) {
7193            mState = UnifiedRestoreState.INITIAL;
7194            mStartRealtime = SystemClock.elapsedRealtime();
7195
7196            mTransport = transport;
7197            mObserver = observer;
7198            mToken = restoreSetToken;
7199            mPmToken = pmToken;
7200            mTargetPackage = targetPackage;
7201            mIsSystemRestore = isFullSystemRestore;
7202            mFinished = false;
7203
7204            if (targetPackage != null) {
7205                // Single package restore
7206                mAcceptSet = new ArrayList<PackageInfo>();
7207                mAcceptSet.add(targetPackage);
7208            } else {
7209                // Everything possible, or a target set
7210                if (filterSet == null) {
7211                    // We want everything and a pony
7212                    List<PackageInfo> apps =
7213                            PackageManagerBackupAgent.getStorableApplications(mPackageManager);
7214                    filterSet = packagesToNames(apps);
7215                    if (DEBUG) {
7216                        Slog.i(TAG, "Full restore; asking for " + filterSet.length + " apps");
7217                    }
7218                }
7219
7220                mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
7221
7222                // Pro tem, we insist on moving the settings provider package to last place.
7223                // Keep track of whether it's in the list, and bump it down if so.  We also
7224                // want to do the system package itself first if it's called for.
7225                boolean hasSystem = false;
7226                boolean hasSettings = false;
7227                for (int i = 0; i < filterSet.length; i++) {
7228                    try {
7229                        PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
7230                        if ("android".equals(info.packageName)) {
7231                            hasSystem = true;
7232                            continue;
7233                        }
7234                        if (SETTINGS_PACKAGE.equals(info.packageName)) {
7235                            hasSettings = true;
7236                            continue;
7237                        }
7238
7239                        if (appIsEligibleForBackup(info.applicationInfo)) {
7240                            mAcceptSet.add(info);
7241                        }
7242                    } catch (NameNotFoundException e) {
7243                        // requested package name doesn't exist; ignore it
7244                    }
7245                }
7246                if (hasSystem) {
7247                    try {
7248                        mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
7249                    } catch (NameNotFoundException e) {
7250                        // won't happen; we know a priori that it's valid
7251                    }
7252                }
7253                if (hasSettings) {
7254                    try {
7255                        mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
7256                    } catch (NameNotFoundException e) {
7257                        // this one is always valid too
7258                    }
7259                }
7260            }
7261
7262            if (MORE_DEBUG) {
7263                Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
7264                for (PackageInfo info : mAcceptSet) {
7265                    Slog.v(TAG, "   " + info.packageName);
7266                }
7267            }
7268        }
7269
7270        private String[] packagesToNames(List<PackageInfo> apps) {
7271            final int N = apps.size();
7272            String[] names = new String[N];
7273            for (int i = 0; i < N; i++) {
7274                names[i] = apps.get(i).packageName;
7275            }
7276            return names;
7277        }
7278
7279        // Execute one tick of whatever state machine the task implements
7280        @Override
7281        public void execute() {
7282            if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState);
7283            switch (mState) {
7284                case INITIAL:
7285                    startRestore();
7286                    break;
7287
7288                case RUNNING_QUEUE:
7289                    dispatchNextRestore();
7290                    break;
7291
7292                case RESTORE_KEYVALUE:
7293                    restoreKeyValue();
7294                    break;
7295
7296                case RESTORE_FULL:
7297                    restoreFull();
7298                    break;
7299
7300                case RESTORE_FINISHED:
7301                    restoreFinished();
7302                    break;
7303
7304                case FINAL:
7305                    if (!mFinished) finalizeRestore();
7306                    else {
7307                        Slog.e(TAG, "Duplicate finish");
7308                    }
7309                    mFinished = true;
7310                    break;
7311            }
7312        }
7313
7314        /*
7315         * SKETCH OF OPERATION
7316         *
7317         * create one of these PerformUnifiedRestoreTask objects, telling it which
7318         * dataset & transport to address, and then parameters within the restore
7319         * operation: single target package vs many, etc.
7320         *
7321         * 1. transport.startRestore(token, list-of-packages).  If we need @pm@  it is
7322         * always placed first and the settings provider always placed last [for now].
7323         *
7324         * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline]
7325         *
7326         *   [ state change => RUNNING_QUEUE ]
7327         *
7328         * NOW ITERATE:
7329         *
7330         * { 3. t.nextRestorePackage()
7331         *   4. does the metadata for this package allow us to restore it?
7332         *      does the on-disk app permit us to restore it? [re-check allowBackup etc]
7333         *   5. is this a key/value dataset?  => key/value agent restore
7334         *       [ state change => RESTORE_KEYVALUE ]
7335         *       5a. spin up agent
7336         *       5b. t.getRestoreData() to stage it properly
7337         *       5c. call into agent to perform restore
7338         *       5d. tear down agent
7339         *       [ state change => RUNNING_QUEUE ]
7340         *
7341         *   6. else it's a stream dataset:
7342         *       [ state change => RESTORE_FULL ]
7343         *       6a. instantiate the engine for a stream restore: engine handles agent lifecycles
7344         *       6b. spin off engine runner on separate thread
7345         *       6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket
7346         *       [ state change => RUNNING_QUEUE ]
7347         * }
7348         *
7349         *   [ state change => FINAL ]
7350         *
7351         * 7. t.finishRestore(), release wakelock, etc.
7352         *
7353         *
7354         */
7355
7356        // state INITIAL : set up for the restore and read the metadata if necessary
7357        private  void startRestore() {
7358            sendStartRestore(mAcceptSet.size());
7359
7360            // If we're starting a full-system restore, set up to begin widget ID remapping
7361            if (mIsSystemRestore) {
7362                AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER);
7363            }
7364
7365            try {
7366                String transportDir = mTransport.transportDirName();
7367                mStateDir = new File(mBaseStateDir, transportDir);
7368
7369                // Fetch the current metadata from the dataset first
7370                PackageInfo pmPackage = new PackageInfo();
7371                pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7372                mAcceptSet.add(0, pmPackage);
7373
7374                PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
7375                mStatus = mTransport.startRestore(mToken, packages);
7376                if (mStatus != BackupTransport.TRANSPORT_OK) {
7377                    Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
7378                    mStatus = BackupTransport.TRANSPORT_ERROR;
7379                    executeNextState(UnifiedRestoreState.FINAL);
7380                    return;
7381                }
7382
7383                RestoreDescription desc = mTransport.nextRestorePackage();
7384                if (desc == null) {
7385                    Slog.e(TAG, "No restore metadata available; halting");
7386                    mStatus = BackupTransport.TRANSPORT_ERROR;
7387                    executeNextState(UnifiedRestoreState.FINAL);
7388                    return;
7389                }
7390                if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
7391                    Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
7392                    mStatus = BackupTransport.TRANSPORT_ERROR;
7393                    executeNextState(UnifiedRestoreState.FINAL);
7394                    return;
7395                }
7396
7397                // Pull the Package Manager metadata from the restore set first
7398                mCurrentPackage = new PackageInfo();
7399                mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7400                mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
7401                mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
7402                if (MORE_DEBUG) {
7403                    Slog.v(TAG, "initiating restore for PMBA");
7404                }
7405                initiateOneRestore(mCurrentPackage, 0);
7406                // The PM agent called operationComplete() already, because our invocation
7407                // of it is process-local and therefore synchronous.  That means that the
7408                // next-state message (RUNNING_QUEUE) is already enqueued.  Only if we're
7409                // unable to proceed with running the queue do we remove that pending
7410                // message and jump straight to the FINAL state.
7411
7412                // Verify that the backup set includes metadata.  If not, we can't do
7413                // signature/version verification etc, so we simply do not proceed with
7414                // the restore operation.
7415                if (!mPmAgent.hasMetadata()) {
7416                    Slog.e(TAG, "No restore metadata available, so not restoring");
7417                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7418                            PACKAGE_MANAGER_SENTINEL,
7419                            "Package manager restore metadata missing");
7420                    mStatus = BackupTransport.TRANSPORT_ERROR;
7421                    mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7422                    executeNextState(UnifiedRestoreState.FINAL);
7423                    return;
7424                }
7425
7426                // Success; cache the metadata and continue as expected with the
7427                // next state already enqueued
7428
7429            } catch (RemoteException e) {
7430                // If we lost the transport at any time, halt
7431                Slog.e(TAG, "Unable to contact transport for restore");
7432                mStatus = BackupTransport.TRANSPORT_ERROR;
7433                mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7434                executeNextState(UnifiedRestoreState.FINAL);
7435                return;
7436            }
7437        }
7438
7439        // state RUNNING_QUEUE : figure out what the next thing to be restored is,
7440        // and fire the appropriate next step
7441        private void dispatchNextRestore() {
7442            UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
7443            try {
7444                mRestoreDescription = mTransport.nextRestorePackage();
7445                final String pkgName = (mRestoreDescription != null)
7446                        ? mRestoreDescription.getPackageName() : null;
7447                if (pkgName == null) {
7448                    Slog.e(TAG, "Failure getting next package name");
7449                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7450                    nextState = UnifiedRestoreState.FINAL;
7451                    return;
7452                } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) {
7453                    // Yay we've reached the end cleanly
7454                    if (DEBUG) {
7455                        Slog.v(TAG, "No more packages; finishing restore");
7456                    }
7457                    int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
7458                    EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
7459                    nextState = UnifiedRestoreState.FINAL;
7460                    return;
7461                }
7462
7463                if (DEBUG) {
7464                    Slog.i(TAG, "Next restore package: " + mRestoreDescription);
7465                }
7466                sendOnRestorePackage(pkgName);
7467
7468                Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName);
7469                if (metaInfo == null) {
7470                    Slog.e(TAG, "No metadata for " + pkgName);
7471                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
7472                            "Package metadata missing");
7473                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7474                    return;
7475                }
7476
7477                try {
7478                    mCurrentPackage = mPackageManager.getPackageInfo(
7479                            pkgName, PackageManager.GET_SIGNATURES);
7480                } catch (NameNotFoundException e) {
7481                    // Whoops, we thought we could restore this package but it
7482                    // turns out not to be present.  Skip it.
7483                    Slog.e(TAG, "Package not present: " + pkgName);
7484                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
7485                            "Package missing on device");
7486                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7487                    return;
7488                }
7489
7490                if (metaInfo.versionCode > mCurrentPackage.versionCode) {
7491                    // Data is from a "newer" version of the app than we have currently
7492                    // installed.  If the app has not declared that it is prepared to
7493                    // handle this case, we do not attempt the restore.
7494                    if ((mCurrentPackage.applicationInfo.flags
7495                            & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
7496                        String message = "Version " + metaInfo.versionCode
7497                                + " > installed version " + mCurrentPackage.versionCode;
7498                        Slog.w(TAG, "Package " + pkgName + ": " + message);
7499                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7500                                pkgName, message);
7501                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
7502                        return;
7503                    } else {
7504                        if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
7505                                + " > installed " + mCurrentPackage.versionCode
7506                                + " but restoreAnyVersion");
7507                    }
7508                }
7509
7510                if (DEBUG) Slog.v(TAG, "Package " + pkgName
7511                        + " restore version [" + metaInfo.versionCode
7512                        + "] is compatible with installed version ["
7513                        + mCurrentPackage.versionCode + "]");
7514
7515                // Reset per-package preconditions and fire the appropriate next state
7516                mWidgetData = null;
7517                final int type = mRestoreDescription.getDataType();
7518                if (type == RestoreDescription.TYPE_KEY_VALUE) {
7519                    nextState = UnifiedRestoreState.RESTORE_KEYVALUE;
7520                } else if (type == RestoreDescription.TYPE_FULL_STREAM) {
7521                    nextState = UnifiedRestoreState.RESTORE_FULL;
7522                } else {
7523                    // Unknown restore type; ignore this package and move on
7524                    Slog.e(TAG, "Unrecognized restore type " + type);
7525                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7526                    return;
7527                }
7528            } catch (RemoteException e) {
7529                Slog.e(TAG, "Can't get next target from transport; ending restore");
7530                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7531                nextState = UnifiedRestoreState.FINAL;
7532                return;
7533            } finally {
7534                executeNextState(nextState);
7535            }
7536        }
7537
7538        // state RESTORE_KEYVALUE : restore one package via key/value API set
7539        private void restoreKeyValue() {
7540            // Initiating the restore will pass responsibility for the state machine's
7541            // progress to the agent callback, so we do not always execute the
7542            // next state here.
7543            final String packageName = mCurrentPackage.packageName;
7544            // Validate some semantic requirements that apply in this way
7545            // only to the key/value restore API flow
7546            if (mCurrentPackage.applicationInfo.backupAgentName == null
7547                    || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
7548                if (DEBUG) {
7549                    Slog.i(TAG, "Data exists for package " + packageName
7550                            + " but app has no agent; skipping");
7551                }
7552                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7553                        "Package has no agent");
7554                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7555                return;
7556            }
7557
7558            Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
7559            if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
7560                Slog.w(TAG, "Signature mismatch restoring " + packageName);
7561                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7562                        "Signature mismatch");
7563                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7564                return;
7565            }
7566
7567            // Good to go!  Set up and bind the agent...
7568            mAgent = bindToAgentSynchronous(
7569                    mCurrentPackage.applicationInfo,
7570                    IApplicationThread.BACKUP_MODE_INCREMENTAL);
7571            if (mAgent == null) {
7572                Slog.w(TAG, "Can't find backup agent for " + packageName);
7573                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7574                        "Restore agent missing");
7575                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7576                return;
7577            }
7578
7579            // And then finally start the restore on this agent
7580            try {
7581                initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
7582                ++mCount;
7583            } catch (Exception e) {
7584                Slog.e(TAG, "Error when attempting restore: " + e.toString());
7585                keyValueAgentErrorCleanup();
7586                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7587            }
7588        }
7589
7590        // Guts of a key/value restore operation
7591        void initiateOneRestore(PackageInfo app, int appVersionCode) {
7592            final String packageName = app.packageName;
7593
7594            if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
7595
7596            // !!! TODO: get the dirs from the transport
7597            mBackupDataName = new File(mDataDir, packageName + ".restore");
7598            mStageName = new File(mDataDir, packageName + ".stage");
7599            mNewStateName = new File(mStateDir, packageName + ".new");
7600            mSavedStateName = new File(mStateDir, packageName);
7601
7602            // don't stage the 'android' package where the wallpaper data lives.  this is
7603            // an optimization: we know there's no widget data hosted/published by that
7604            // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
7605            // data following the download.
7606            boolean staging = !packageName.equals("android");
7607            ParcelFileDescriptor stage;
7608            File downloadFile = (staging) ? mStageName : mBackupDataName;
7609
7610            final int token = generateToken();
7611            try {
7612                // Run the transport's restore pass
7613                stage = ParcelFileDescriptor.open(downloadFile,
7614                        ParcelFileDescriptor.MODE_READ_WRITE |
7615                        ParcelFileDescriptor.MODE_CREATE |
7616                        ParcelFileDescriptor.MODE_TRUNCATE);
7617
7618                if (!SELinux.restorecon(mBackupDataName)) {
7619                    Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
7620                }
7621
7622                if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
7623                    // Transport-level failure, so we wind everything up and
7624                    // terminate the restore operation.
7625                    Slog.e(TAG, "Error getting restore data for " + packageName);
7626                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7627                    stage.close();
7628                    downloadFile.delete();
7629                    executeNextState(UnifiedRestoreState.FINAL);
7630                    return;
7631                }
7632
7633                // We have the data from the transport. Now we extract and strip
7634                // any per-package metadata (typically widget-related information)
7635                // if appropriate
7636                if (staging) {
7637                    stage.close();
7638                    stage = ParcelFileDescriptor.open(downloadFile,
7639                            ParcelFileDescriptor.MODE_READ_ONLY);
7640
7641                    mBackupData = ParcelFileDescriptor.open(mBackupDataName,
7642                            ParcelFileDescriptor.MODE_READ_WRITE |
7643                            ParcelFileDescriptor.MODE_CREATE |
7644                            ParcelFileDescriptor.MODE_TRUNCATE);
7645
7646                    BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
7647                    BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
7648                    byte[] buffer = new byte[8192]; // will grow when needed
7649                    while (in.readNextHeader()) {
7650                        final String key = in.getKey();
7651                        final int size = in.getDataSize();
7652
7653                        // is this a special key?
7654                        if (key.equals(KEY_WIDGET_STATE)) {
7655                            if (DEBUG) {
7656                                Slog.i(TAG, "Restoring widget state for " + packageName);
7657                            }
7658                            mWidgetData = new byte[size];
7659                            in.readEntityData(mWidgetData, 0, size);
7660                        } else {
7661                            if (size > buffer.length) {
7662                                buffer = new byte[size];
7663                            }
7664                            in.readEntityData(buffer, 0, size);
7665                            out.writeEntityHeader(key, size);
7666                            out.writeEntityData(buffer, size);
7667                        }
7668                    }
7669
7670                    mBackupData.close();
7671                }
7672
7673                // Okay, we have the data.  Now have the agent do the restore.
7674                stage.close();
7675                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
7676                        ParcelFileDescriptor.MODE_READ_ONLY);
7677
7678                mNewState = ParcelFileDescriptor.open(mNewStateName,
7679                        ParcelFileDescriptor.MODE_READ_WRITE |
7680                        ParcelFileDescriptor.MODE_CREATE |
7681                        ParcelFileDescriptor.MODE_TRUNCATE);
7682
7683                // Kick off the restore, checking for hung agents.  The timeout or
7684                // the operationComplete() callback will schedule the next step,
7685                // so we do not do that here.
7686                prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
7687                mAgent.doRestore(mBackupData, appVersionCode, mNewState,
7688                        token, mBackupManagerBinder);
7689            } catch (Exception e) {
7690                Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
7691                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7692                        packageName, e.toString());
7693                keyValueAgentErrorCleanup();    // clears any pending timeout messages as well
7694
7695                // After a restore failure we go back to running the queue.  If there
7696                // are no more packages to be restored that will be handled by the
7697                // next step.
7698                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7699            }
7700        }
7701
7702        // state RESTORE_FULL : restore one package via streaming engine
7703        private void restoreFull() {
7704            // None of this can run on the work looper here, so we spin asynchronous
7705            // work like this:
7706            //
7707            //   StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk()
7708            //                       write it into the pipe to the engine
7709            //   EngineThread: FullRestoreEngine thread communicating with the target app
7710            //
7711            // When finished, StreamFeederThread executes next state as appropriate on the
7712            // backup looper, and the overall unified restore task resumes
7713            try {
7714                StreamFeederThread feeder = new StreamFeederThread();
7715                if (DEBUG) {
7716                    Slog.i(TAG, "Spinning threads for stream restore of "
7717                            + mCurrentPackage.packageName);
7718                }
7719                new Thread(feeder, "unified-stream-feeder").start();
7720
7721                // At this point the feeder is responsible for advancing the restore
7722                // state, so we're done here.
7723            } catch (IOException e) {
7724                // Unable to instantiate the feeder thread -- we need to bail on the
7725                // current target.  We haven't asked the transport for data yet, though,
7726                // so we can do that simply by going back to running the restore queue.
7727                Slog.e(TAG, "Unable to construct pipes for stream restore!");
7728                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7729            }
7730        }
7731
7732        // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
7733        private void restoreFinished() {
7734            try {
7735                final int token = generateToken();
7736                prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
7737                mAgent.doRestoreFinished(token, mBackupManagerBinder);
7738                // If we get this far, the callback or timeout will schedule the
7739                // next restore state, so we're done
7740            } catch (Exception e) {
7741                Slog.e(TAG, "Unable to finalize restore of " + mCurrentPackage.packageName);
7742                executeNextState(UnifiedRestoreState.FINAL);
7743            }
7744        }
7745
7746        class StreamFeederThread extends RestoreEngine implements Runnable {
7747            final String TAG = "StreamFeederThread";
7748            FullRestoreEngine mEngine;
7749
7750            // pipe through which we read data from the transport. [0] read, [1] write
7751            ParcelFileDescriptor[] mTransportPipes;
7752
7753            // pipe through which the engine will read data.  [0] read, [1] write
7754            ParcelFileDescriptor[] mEnginePipes;
7755
7756            public StreamFeederThread() throws IOException {
7757                mTransportPipes = ParcelFileDescriptor.createPipe();
7758                mEnginePipes = ParcelFileDescriptor.createPipe();
7759                setRunning(true);
7760            }
7761
7762            @Override
7763            public void run() {
7764                UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
7765                int status = BackupTransport.TRANSPORT_OK;
7766
7767                EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
7768                        mCurrentPackage.packageName);
7769
7770                mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false);
7771                EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]);
7772
7773                ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
7774                ParcelFileDescriptor tReadEnd = mTransportPipes[0];
7775                ParcelFileDescriptor tWriteEnd = mTransportPipes[1];
7776
7777                int bufferSize = 32 * 1024;
7778                byte[] buffer = new byte[bufferSize];
7779                FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor());
7780                FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor());
7781
7782                // spin up the engine and start moving data to it
7783                new Thread(eThread, "unified-restore-engine").start();
7784
7785                try {
7786                    while (status == BackupTransport.TRANSPORT_OK) {
7787                        // have the transport write some of the restoring data to us
7788                        int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd);
7789                        if (result > 0) {
7790                            // The transport wrote this many bytes of restore data to the
7791                            // pipe, so pass it along to the engine.
7792                            if (MORE_DEBUG) {
7793                                Slog.v(TAG, "  <- transport provided chunk size " + result);
7794                            }
7795                            if (result > bufferSize) {
7796                                bufferSize = result;
7797                                buffer = new byte[bufferSize];
7798                            }
7799                            int toCopy = result;
7800                            while (toCopy > 0) {
7801                                int n = transportIn.read(buffer, 0, toCopy);
7802                                engineOut.write(buffer, 0, n);
7803                                toCopy -= n;
7804                                if (MORE_DEBUG) {
7805                                    Slog.v(TAG, "  -> wrote " + n + " to engine, left=" + toCopy);
7806                                }
7807                            }
7808                        } else if (result == BackupTransport.NO_MORE_DATA) {
7809                            // Clean finish.  Wind up and we're done!
7810                            if (MORE_DEBUG) {
7811                                Slog.i(TAG, "Got clean full-restore EOF for "
7812                                        + mCurrentPackage.packageName);
7813                            }
7814                            status = BackupTransport.TRANSPORT_OK;
7815                            break;
7816                        } else {
7817                            // Transport reported some sort of failure; the fall-through
7818                            // handling will deal properly with that.
7819                            Slog.e(TAG, "Error " + result + " streaming restore for "
7820                                    + mCurrentPackage.packageName);
7821                            EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7822                            status = result;
7823                        }
7824                    }
7825                    if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through");
7826                } catch (IOException e) {
7827                    // We lost our ability to communicate via the pipes.  That's worrying
7828                    // but potentially recoverable; abandon this package's restore but
7829                    // carry on with the next restore target.
7830                    Slog.e(TAG, "Unable to route data for restore");
7831                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7832                            mCurrentPackage.packageName, "I/O error on pipes");
7833                    status = BackupTransport.AGENT_ERROR;
7834                } catch (RemoteException e) {
7835                    // The transport went away; terminate the whole operation.  Closing
7836                    // the sockets will wake up the engine and it will then tidy up the
7837                    // remote end.
7838                    Slog.e(TAG, "Transport failed during restore");
7839                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7840                    status = BackupTransport.TRANSPORT_ERROR;
7841                } finally {
7842                    // Close the transport pipes and *our* end of the engine pipe,
7843                    // but leave the engine thread's end open so that it properly
7844                    // hits EOF and winds up its operations.
7845                    IoUtils.closeQuietly(mEnginePipes[1]);
7846                    IoUtils.closeQuietly(mTransportPipes[0]);
7847                    IoUtils.closeQuietly(mTransportPipes[1]);
7848
7849                    // Don't proceed until the engine has torn down the agent etc
7850                    eThread.waitForResult();
7851
7852                    if (MORE_DEBUG) {
7853                        Slog.i(TAG, "engine thread finished; proceeding");
7854                    }
7855
7856                    // Now we're really done with this one too
7857                    IoUtils.closeQuietly(mEnginePipes[0]);
7858
7859                    // If we hit a transport-level error, we are done with everything;
7860                    // if we hit an agent error we just go back to running the queue.
7861                    if (status == BackupTransport.TRANSPORT_OK) {
7862                        // Clean finish, so just carry on
7863                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
7864                    } else {
7865                        // Something went wrong somewhere.  Whether it was at the transport
7866                        // level is immaterial; we need to tell the transport to bail
7867                        try {
7868                            mTransport.abortFullRestore();
7869                        } catch (RemoteException e) {
7870                            // transport itself is dead; make sure we handle this as a
7871                            // fatal error
7872                            status = BackupTransport.TRANSPORT_ERROR;
7873                        }
7874
7875                        // We also need to wipe the current target's data, as it's probably
7876                        // in an incoherent state.
7877                        clearApplicationDataSynchronous(mCurrentPackage.packageName);
7878
7879                        // Schedule the next state based on the nature of our failure
7880                        if (status == BackupTransport.TRANSPORT_ERROR) {
7881                            nextState = UnifiedRestoreState.FINAL;
7882                        } else {
7883                            nextState = UnifiedRestoreState.RUNNING_QUEUE;
7884                        }
7885                    }
7886                    executeNextState(nextState);
7887                    setRunning(false);
7888                }
7889            }
7890
7891        }
7892
7893        class EngineThread implements Runnable {
7894            FullRestoreEngine mEngine;
7895            FileInputStream mEngineStream;
7896
7897            EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
7898                mEngine = engine;
7899                engine.setRunning(true);
7900                mEngineStream = new FileInputStream(engineSocket.getFileDescriptor());
7901            }
7902
7903            public boolean isRunning() {
7904                return mEngine.isRunning();
7905            }
7906
7907            public int waitForResult() {
7908                return mEngine.waitForResult();
7909            }
7910
7911            @Override
7912            public void run() {
7913                while (mEngine.isRunning()) {
7914                    mEngine.restoreOneFile(mEngineStream);
7915                }
7916            }
7917        }
7918
7919        // state FINAL : tear everything down and we're done.
7920        private void finalizeRestore() {
7921            if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
7922
7923            try {
7924                mTransport.finishRestore();
7925            } catch (Exception e) {
7926                Slog.e(TAG, "Error finishing restore", e);
7927            }
7928
7929            // Tell the observer we're done
7930            if (mObserver != null) {
7931                try {
7932                    mObserver.restoreFinished(mStatus);
7933                } catch (RemoteException e) {
7934                    Slog.d(TAG, "Restore observer died at restoreFinished");
7935                }
7936            }
7937
7938            // Clear any ongoing session timeout.
7939            mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
7940
7941            // If we have a PM token, we must under all circumstances be sure to
7942            // handshake when we've finished.
7943            if (mPmToken > 0) {
7944                if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
7945                try {
7946                    mPackageManagerBinder.finishPackageInstall(mPmToken);
7947                } catch (RemoteException e) { /* can't happen */ }
7948            } else {
7949                // We were invoked via an active restore session, not by the Package
7950                // Manager, so start up the session timeout again.
7951                mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
7952                        TIMEOUT_RESTORE_INTERVAL);
7953            }
7954
7955            // Kick off any work that may be needed regarding app widget restores
7956            AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER);
7957
7958            // If this was a full-system restore, record the ancestral
7959            // dataset information
7960            if (mIsSystemRestore && mPmAgent != null) {
7961                mAncestralPackages = mPmAgent.getRestoredPackages();
7962                mAncestralToken = mToken;
7963                writeRestoreTokens();
7964            }
7965
7966            // done; we can finally release the wakelock and be legitimately done.
7967            Slog.i(TAG, "Restore complete.");
7968            mWakelock.release();
7969        }
7970
7971        void keyValueAgentErrorCleanup() {
7972            // If the agent fails restore, it might have put the app's data
7973            // into an incoherent state.  For consistency we wipe its data
7974            // again in this case before continuing with normal teardown
7975            clearApplicationDataSynchronous(mCurrentPackage.packageName);
7976            keyValueAgentCleanup();
7977        }
7978
7979        void keyValueAgentCleanup() {
7980            mBackupDataName.delete();
7981            mStageName.delete();
7982            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
7983            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
7984            mBackupData = mNewState = null;
7985
7986            // if everything went okay, remember the recorded state now
7987            //
7988            // !!! TODO: the restored data could be migrated on the server
7989            // side into the current dataset.  In that case the new state file
7990            // we just created would reflect the data already extant in the
7991            // backend, so there'd be nothing more to do.  Until that happens,
7992            // however, we need to make sure that we record the data to the
7993            // current backend dataset.  (Yes, this means shipping the data over
7994            // the wire in both directions.  That's bad, but consistency comes
7995            // first, then efficiency.)  Once we introduce server-side data
7996            // migration to the newly-restored device's dataset, we will change
7997            // the following from a discard of the newly-written state to the
7998            // "correct" operation of renaming into the canonical state blob.
7999            mNewStateName.delete();                      // TODO: remove; see above comment
8000            //mNewStateName.renameTo(mSavedStateName);   // TODO: replace with this
8001
8002            // If this wasn't the PM pseudopackage, tear down the agent side
8003            if (mCurrentPackage.applicationInfo != null) {
8004                // unbind and tidy up even on timeout or failure
8005                try {
8006                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
8007
8008                    // The agent was probably running with a stub Application object,
8009                    // which isn't a valid run mode for the main app logic.  Shut
8010                    // down the app so that next time it's launched, it gets the
8011                    // usual full initialization.  Note that this is only done for
8012                    // full-system restores: when a single app has requested a restore,
8013                    // it is explicitly not killed following that operation.
8014                    if (mTargetPackage == null && (mCurrentPackage.applicationInfo.flags
8015                            & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
8016                        if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
8017                                + mCurrentPackage.applicationInfo.processName);
8018                        mActivityManager.killApplicationProcess(
8019                                mCurrentPackage.applicationInfo.processName,
8020                                mCurrentPackage.applicationInfo.uid);
8021                    }
8022                } catch (RemoteException e) {
8023                    // can't happen; we run in the same process as the activity manager
8024                }
8025            }
8026
8027            // The caller is responsible for reestablishing the state machine; our
8028            // responsibility here is to clear the decks for whatever comes next.
8029            mBackupHandler.removeMessages(MSG_TIMEOUT, this);
8030            synchronized (mCurrentOpLock) {
8031                mCurrentOperations.clear();
8032            }
8033        }
8034
8035        @Override
8036        public void operationComplete(int unusedResult) {
8037            if (MORE_DEBUG) {
8038                Slog.i(TAG, "operationComplete() during restore: target="
8039                        + mCurrentPackage.packageName
8040                        + " state=" + mState);
8041            }
8042
8043            final UnifiedRestoreState nextState;
8044            switch (mState) {
8045                case INITIAL:
8046                    // We've just (manually) restored the PMBA.  It doesn't need the
8047                    // additional restore-finished callback so we bypass that and go
8048                    // directly to running the queue.
8049                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8050                    break;
8051
8052                case RESTORE_KEYVALUE:
8053                case RESTORE_FULL: {
8054                    // Okay, we've just heard back from the agent that it's done with
8055                    // the restore itself.  We now have to send the same agent its
8056                    // doRestoreFinished() callback, so roll into that state.
8057                    nextState = UnifiedRestoreState.RESTORE_FINISHED;
8058                    break;
8059                }
8060
8061                case RESTORE_FINISHED: {
8062                    // Okay, we're done with this package.  Tidy up and go on to the next
8063                    // app in the queue.
8064                    int size = (int) mBackupDataName.length();
8065                    EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
8066                            mCurrentPackage.packageName, size);
8067
8068                    // Just go back to running the restore queue
8069                    keyValueAgentCleanup();
8070
8071                    // If there was widget state associated with this app, get the OS to
8072                    // incorporate it into current bookeeping and then pass that along to
8073                    // the app as part of the restore-time work.
8074                    if (mWidgetData != null) {
8075                        restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
8076                    }
8077
8078                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8079                    break;
8080                }
8081
8082                default: {
8083                    // Some kind of horrible semantic error; we're in an unexpected state.
8084                    // Back off hard and wind up.
8085                    Slog.e(TAG, "Unexpected restore callback into state " + mState);
8086                    keyValueAgentErrorCleanup();
8087                    nextState = UnifiedRestoreState.FINAL;
8088                    break;
8089                }
8090            }
8091
8092            executeNextState(nextState);
8093        }
8094
8095        // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
8096        @Override
8097        public void handleTimeout() {
8098            Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
8099            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8100                    mCurrentPackage.packageName, "restore timeout");
8101            // Handle like an agent that threw on invocation: wipe it and go on to the next
8102            keyValueAgentErrorCleanup();
8103            executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8104        }
8105
8106        void executeNextState(UnifiedRestoreState nextState) {
8107            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
8108                    + this + " nextState=" + nextState);
8109            mState = nextState;
8110            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
8111            mBackupHandler.sendMessage(msg);
8112        }
8113
8114        // restore observer support
8115        void sendStartRestore(int numPackages) {
8116            if (mObserver != null) {
8117                try {
8118                    mObserver.restoreStarting(numPackages);
8119                } catch (RemoteException e) {
8120                    Slog.w(TAG, "Restore observer went away: startRestore");
8121                    mObserver = null;
8122                }
8123            }
8124        }
8125
8126        void sendOnRestorePackage(String name) {
8127            if (mObserver != null) {
8128                if (mObserver != null) {
8129                    try {
8130                        mObserver.onUpdate(mCount, name);
8131                    } catch (RemoteException e) {
8132                        Slog.d(TAG, "Restore observer died in onUpdate");
8133                        mObserver = null;
8134                    }
8135                }
8136            }
8137        }
8138
8139        void sendEndRestore() {
8140            if (mObserver != null) {
8141                try {
8142                    mObserver.restoreFinished(mStatus);
8143                } catch (RemoteException e) {
8144                    Slog.w(TAG, "Restore observer went away: endRestore");
8145                    mObserver = null;
8146                }
8147            }
8148        }
8149    }
8150
8151    class PerformClearTask implements Runnable {
8152        IBackupTransport mTransport;
8153        PackageInfo mPackage;
8154
8155        PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
8156            mTransport = transport;
8157            mPackage = packageInfo;
8158        }
8159
8160        public void run() {
8161            try {
8162                // Clear the on-device backup state to ensure a full backup next time
8163                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
8164                File stateFile = new File(stateDir, mPackage.packageName);
8165                stateFile.delete();
8166
8167                // Tell the transport to remove all the persistent storage for the app
8168                // TODO - need to handle failures
8169                mTransport.clearBackupData(mPackage);
8170            } catch (RemoteException e) {
8171                // can't happen; the transport is local
8172            } catch (Exception e) {
8173                Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
8174            } finally {
8175                try {
8176                    // TODO - need to handle failures
8177                    mTransport.finishBackup();
8178                } catch (RemoteException e) {
8179                    // can't happen; the transport is local
8180                }
8181
8182                // Last but not least, release the cpu
8183                mWakelock.release();
8184            }
8185        }
8186    }
8187
8188    class PerformInitializeTask implements Runnable {
8189        HashSet<String> mQueue;
8190
8191        PerformInitializeTask(HashSet<String> transportNames) {
8192            mQueue = transportNames;
8193        }
8194
8195        public void run() {
8196            try {
8197                for (String transportName : mQueue) {
8198                    IBackupTransport transport = getTransport(transportName);
8199                    if (transport == null) {
8200                        Slog.e(TAG, "Requested init for " + transportName + " but not found");
8201                        continue;
8202                    }
8203
8204                    Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
8205                    EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
8206                    long startRealtime = SystemClock.elapsedRealtime();
8207                    int status = transport.initializeDevice();
8208
8209                    if (status == BackupTransport.TRANSPORT_OK) {
8210                        status = transport.finishBackup();
8211                    }
8212
8213                    // Okay, the wipe really happened.  Clean up our local bookkeeping.
8214                    if (status == BackupTransport.TRANSPORT_OK) {
8215                        Slog.i(TAG, "Device init successful");
8216                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
8217                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
8218                        resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
8219                        EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
8220                        synchronized (mQueueLock) {
8221                            recordInitPendingLocked(false, transportName);
8222                        }
8223                    } else {
8224                        // If this didn't work, requeue this one and try again
8225                        // after a suitable interval
8226                        Slog.e(TAG, "Transport error in initializeDevice()");
8227                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
8228                        synchronized (mQueueLock) {
8229                            recordInitPendingLocked(true, transportName);
8230                        }
8231                        // do this via another alarm to make sure of the wakelock states
8232                        long delay = transport.requestBackupTime();
8233                        if (DEBUG) Slog.w(TAG, "init failed on "
8234                                + transportName + " resched in " + delay);
8235                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
8236                                System.currentTimeMillis() + delay, mRunInitIntent);
8237                    }
8238                }
8239            } catch (RemoteException e) {
8240                // can't happen; the transports are local
8241            } catch (Exception e) {
8242                Slog.e(TAG, "Unexpected error performing init", e);
8243            } finally {
8244                // Done; release the wakelock
8245                mWakelock.release();
8246            }
8247        }
8248    }
8249
8250    private void dataChangedImpl(String packageName) {
8251        HashSet<String> targets = dataChangedTargets(packageName);
8252        dataChangedImpl(packageName, targets);
8253    }
8254
8255    private void dataChangedImpl(String packageName, HashSet<String> targets) {
8256        // Record that we need a backup pass for the caller.  Since multiple callers
8257        // may share a uid, we need to note all candidates within that uid and schedule
8258        // a backup pass for each of them.
8259        if (targets == null) {
8260            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8261                   + " uid=" + Binder.getCallingUid());
8262            return;
8263        }
8264
8265        synchronized (mQueueLock) {
8266            // Note that this client has made data changes that need to be backed up
8267            if (targets.contains(packageName)) {
8268                // Add the caller to the set of pending backups.  If there is
8269                // one already there, then overwrite it, but no harm done.
8270                BackupRequest req = new BackupRequest(packageName);
8271                if (mPendingBackups.put(packageName, req) == null) {
8272                    if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
8273
8274                    // Journal this request in case of crash.  The put()
8275                    // operation returned null when this package was not already
8276                    // in the set; we want to avoid touching the disk redundantly.
8277                    writeToJournalLocked(packageName);
8278
8279                    if (MORE_DEBUG) {
8280                        int numKeys = mPendingBackups.size();
8281                        Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
8282                        for (BackupRequest b : mPendingBackups.values()) {
8283                            Slog.d(TAG, "    + " + b);
8284                        }
8285                    }
8286                }
8287            }
8288        }
8289
8290        // ...and schedule a backup pass if necessary
8291        KeyValueBackupJob.schedule(mContext);
8292    }
8293
8294    // Note: packageName is currently unused, but may be in the future
8295    private HashSet<String> dataChangedTargets(String packageName) {
8296        // If the caller does not hold the BACKUP permission, it can only request a
8297        // backup of its own data.
8298        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8299                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8300            synchronized (mBackupParticipants) {
8301                return mBackupParticipants.get(Binder.getCallingUid());
8302            }
8303        }
8304
8305        // a caller with full permission can ask to back up any participating app
8306        // !!! TODO: allow backup of ANY app?
8307        HashSet<String> targets = new HashSet<String>();
8308        synchronized (mBackupParticipants) {
8309            int N = mBackupParticipants.size();
8310            for (int i = 0; i < N; i++) {
8311                HashSet<String> s = mBackupParticipants.valueAt(i);
8312                if (s != null) {
8313                    targets.addAll(s);
8314                }
8315            }
8316        }
8317        return targets;
8318    }
8319
8320    private void writeToJournalLocked(String str) {
8321        RandomAccessFile out = null;
8322        try {
8323            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
8324            out = new RandomAccessFile(mJournal, "rws");
8325            out.seek(out.length());
8326            out.writeUTF(str);
8327        } catch (IOException e) {
8328            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
8329            mJournal = null;
8330        } finally {
8331            try { if (out != null) out.close(); } catch (IOException e) {}
8332        }
8333    }
8334
8335    // ----- IBackupManager binder interface -----
8336
8337    public void dataChanged(final String packageName) {
8338        final int callingUserHandle = UserHandle.getCallingUserId();
8339        if (callingUserHandle != UserHandle.USER_OWNER) {
8340            // App is running under a non-owner user profile.  For now, we do not back
8341            // up data from secondary user profiles.
8342            // TODO: backups for all user profiles.
8343            if (MORE_DEBUG) {
8344                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
8345                        + callingUserHandle);
8346            }
8347            return;
8348        }
8349
8350        final HashSet<String> targets = dataChangedTargets(packageName);
8351        if (targets == null) {
8352            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8353                   + " uid=" + Binder.getCallingUid());
8354            return;
8355        }
8356
8357        mBackupHandler.post(new Runnable() {
8358                public void run() {
8359                    dataChangedImpl(packageName, targets);
8360                }
8361            });
8362    }
8363
8364    // Clear the given package's backup data from the current transport
8365    public void clearBackupData(String transportName, String packageName) {
8366        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
8367        PackageInfo info;
8368        try {
8369            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
8370        } catch (NameNotFoundException e) {
8371            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
8372            return;
8373        }
8374
8375        // If the caller does not hold the BACKUP permission, it can only request a
8376        // wipe of its own backed-up data.
8377        HashSet<String> apps;
8378        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8379                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8380            apps = mBackupParticipants.get(Binder.getCallingUid());
8381        } else {
8382            // a caller with full permission can ask to back up any participating app
8383            // !!! TODO: allow data-clear of ANY app?
8384            if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
8385            apps = new HashSet<String>();
8386            int N = mBackupParticipants.size();
8387            for (int i = 0; i < N; i++) {
8388                HashSet<String> s = mBackupParticipants.valueAt(i);
8389                if (s != null) {
8390                    apps.addAll(s);
8391                }
8392            }
8393        }
8394
8395        // Is the given app an available participant?
8396        if (apps.contains(packageName)) {
8397            // found it; fire off the clear request
8398            if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
8399            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
8400            synchronized (mQueueLock) {
8401                final IBackupTransport transport = getTransport(transportName);
8402                if (transport == null) {
8403                    // transport is currently unavailable -- make sure to retry
8404                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
8405                            new ClearRetryParams(transportName, packageName));
8406                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
8407                    return;
8408                }
8409                long oldId = Binder.clearCallingIdentity();
8410                mWakelock.acquire();
8411                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
8412                        new ClearParams(transport, info));
8413                mBackupHandler.sendMessage(msg);
8414                Binder.restoreCallingIdentity(oldId);
8415            }
8416        }
8417    }
8418
8419    // Run a backup pass immediately for any applications that have declared
8420    // that they have pending updates.
8421    public void backupNow() {
8422        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
8423
8424        if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
8425        synchronized (mQueueLock) {
8426            // Fire the intent that kicks off the whole shebang...
8427            try {
8428                mRunBackupIntent.send();
8429            } catch (PendingIntent.CanceledException e) {
8430                // should never happen
8431                Slog.e(TAG, "run-backup intent cancelled!");
8432            }
8433
8434            // ...and cancel any pending scheduled job, because we've just superseded it
8435            KeyValueBackupJob.cancel(mContext);
8436        }
8437    }
8438
8439    boolean deviceIsProvisioned() {
8440        final ContentResolver resolver = mContext.getContentResolver();
8441        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
8442    }
8443
8444    // Run a *full* backup pass for the given packages, writing the resulting data stream
8445    // to the supplied file descriptor.  This method is synchronous and does not return
8446    // to the caller until the backup has been completed.
8447    //
8448    // This is the variant used by 'adb backup'; it requires on-screen confirmation
8449    // by the user because it can be used to offload data over untrusted USB.
8450    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
8451            boolean includeObbs, boolean includeShared, boolean doWidgets,
8452            boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) {
8453        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
8454
8455        final int callingUserHandle = UserHandle.getCallingUserId();
8456        if (callingUserHandle != UserHandle.USER_OWNER) {
8457            throw new IllegalStateException("Backup supported only for the device owner");
8458        }
8459
8460        // Validate
8461        if (!doAllApps) {
8462            if (!includeShared) {
8463                // If we're backing up shared data (sdcard or equivalent), then we can run
8464                // without any supplied app names.  Otherwise, we'd be doing no work, so
8465                // report the error.
8466                if (pkgList == null || pkgList.length == 0) {
8467                    throw new IllegalArgumentException(
8468                            "Backup requested but neither shared nor any apps named");
8469                }
8470            }
8471        }
8472
8473        long oldId = Binder.clearCallingIdentity();
8474        try {
8475            // Doesn't make sense to do a full backup prior to setup
8476            if (!deviceIsProvisioned()) {
8477                Slog.i(TAG, "Full backup not supported before setup");
8478                return;
8479            }
8480
8481            if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
8482                    + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
8483                    + " system=" + includeSystem + " pkgs=" + pkgList);
8484            Slog.i(TAG, "Beginning full backup...");
8485
8486            FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
8487                    includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList);
8488            final int token = generateToken();
8489            synchronized (mFullConfirmations) {
8490                mFullConfirmations.put(token, params);
8491            }
8492
8493            // start up the confirmation UI
8494            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
8495            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
8496                Slog.e(TAG, "Unable to launch full backup confirmation");
8497                mFullConfirmations.delete(token);
8498                return;
8499            }
8500
8501            // make sure the screen is lit for the user interaction
8502            mPowerManager.userActivity(SystemClock.uptimeMillis(),
8503                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
8504                    0);
8505
8506            // start the confirmation countdown
8507            startConfirmationTimeout(token, params);
8508
8509            // wait for the backup to be performed
8510            if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
8511            waitForCompletion(params);
8512        } finally {
8513            try {
8514                fd.close();
8515            } catch (IOException e) {
8516                // just eat it
8517            }
8518            Binder.restoreCallingIdentity(oldId);
8519            Slog.d(TAG, "Full backup processing complete.");
8520        }
8521    }
8522
8523    public void fullTransportBackup(String[] pkgNames) {
8524        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
8525                "fullTransportBackup");
8526
8527        final int callingUserHandle = UserHandle.getCallingUserId();
8528        if (callingUserHandle != UserHandle.USER_OWNER) {
8529            throw new IllegalStateException("Restore supported only for the device owner");
8530        }
8531
8532        if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
8533            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
8534        } else {
8535            if (DEBUG) {
8536                Slog.d(TAG, "fullTransportBackup()");
8537            }
8538
8539            CountDownLatch latch = new CountDownLatch(1);
8540            PerformFullTransportBackupTask task =
8541                    new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
8542            (new Thread(task, "full-transport-master")).start();
8543            do {
8544                try {
8545                    latch.await();
8546                    break;
8547                } catch (InterruptedException e) {
8548                    // Just go back to waiting for the latch to indicate completion
8549                }
8550            } while (true);
8551
8552            // We just ran a backup on these packages, so kick them to the end of the queue
8553            final long now = System.currentTimeMillis();
8554            for (String pkg : pkgNames) {
8555                enqueueFullBackup(pkg, now);
8556            }
8557        }
8558
8559        if (DEBUG) {
8560            Slog.d(TAG, "Done with full transport backup.");
8561        }
8562    }
8563
8564    public void fullRestore(ParcelFileDescriptor fd) {
8565        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
8566
8567        final int callingUserHandle = UserHandle.getCallingUserId();
8568        if (callingUserHandle != UserHandle.USER_OWNER) {
8569            throw new IllegalStateException("Restore supported only for the device owner");
8570        }
8571
8572        long oldId = Binder.clearCallingIdentity();
8573
8574        try {
8575            // Check whether the device has been provisioned -- we don't handle
8576            // full restores prior to completing the setup process.
8577            if (!deviceIsProvisioned()) {
8578                Slog.i(TAG, "Full restore not permitted before setup");
8579                return;
8580            }
8581
8582            Slog.i(TAG, "Beginning full restore...");
8583
8584            FullRestoreParams params = new FullRestoreParams(fd);
8585            final int token = generateToken();
8586            synchronized (mFullConfirmations) {
8587                mFullConfirmations.put(token, params);
8588            }
8589
8590            // start up the confirmation UI
8591            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
8592            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
8593                Slog.e(TAG, "Unable to launch full restore confirmation");
8594                mFullConfirmations.delete(token);
8595                return;
8596            }
8597
8598            // make sure the screen is lit for the user interaction
8599            mPowerManager.userActivity(SystemClock.uptimeMillis(),
8600                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
8601                    0);
8602
8603            // start the confirmation countdown
8604            startConfirmationTimeout(token, params);
8605
8606            // wait for the restore to be performed
8607            if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
8608            waitForCompletion(params);
8609        } finally {
8610            try {
8611                fd.close();
8612            } catch (IOException e) {
8613                Slog.w(TAG, "Error trying to close fd after full restore: " + e);
8614            }
8615            Binder.restoreCallingIdentity(oldId);
8616            Slog.i(TAG, "Full restore processing complete.");
8617        }
8618    }
8619
8620    boolean startConfirmationUi(int token, String action) {
8621        try {
8622            Intent confIntent = new Intent(action);
8623            confIntent.setClassName("com.android.backupconfirm",
8624                    "com.android.backupconfirm.BackupRestoreConfirmation");
8625            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
8626            confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
8627            mContext.startActivity(confIntent);
8628        } catch (ActivityNotFoundException e) {
8629            return false;
8630        }
8631        return true;
8632    }
8633
8634    void startConfirmationTimeout(int token, FullParams params) {
8635        if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
8636                + TIMEOUT_FULL_CONFIRMATION + " millis");
8637        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
8638                token, 0, params);
8639        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
8640    }
8641
8642    void waitForCompletion(FullParams params) {
8643        synchronized (params.latch) {
8644            while (params.latch.get() == false) {
8645                try {
8646                    params.latch.wait();
8647                } catch (InterruptedException e) { /* never interrupted */ }
8648            }
8649        }
8650    }
8651
8652    void signalFullBackupRestoreCompletion(FullParams params) {
8653        synchronized (params.latch) {
8654            params.latch.set(true);
8655            params.latch.notifyAll();
8656        }
8657    }
8658
8659    // Confirm that the previously-requested full backup/restore operation can proceed.  This
8660    // is used to require a user-facing disclosure about the operation.
8661    public void acknowledgeFullBackupOrRestore(int token, boolean allow,
8662            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
8663        if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
8664                + " allow=" + allow);
8665
8666        // TODO: possibly require not just this signature-only permission, but even
8667        // require that the specific designated confirmation-UI app uid is the caller?
8668        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
8669
8670        long oldId = Binder.clearCallingIdentity();
8671        try {
8672
8673            FullParams params;
8674            synchronized (mFullConfirmations) {
8675                params = mFullConfirmations.get(token);
8676                if (params != null) {
8677                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
8678                    mFullConfirmations.delete(token);
8679
8680                    if (allow) {
8681                        final int verb = params instanceof FullBackupParams
8682                                ? MSG_RUN_ADB_BACKUP
8683                                : MSG_RUN_ADB_RESTORE;
8684
8685                        params.observer = observer;
8686                        params.curPassword = curPassword;
8687
8688                        params.encryptPassword = encPpassword;
8689
8690                        if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
8691                        mWakelock.acquire();
8692                        Message msg = mBackupHandler.obtainMessage(verb, params);
8693                        mBackupHandler.sendMessage(msg);
8694                    } else {
8695                        Slog.w(TAG, "User rejected full backup/restore operation");
8696                        // indicate completion without having actually transferred any data
8697                        signalFullBackupRestoreCompletion(params);
8698                    }
8699                } else {
8700                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
8701                }
8702            }
8703        } finally {
8704            Binder.restoreCallingIdentity(oldId);
8705        }
8706    }
8707
8708    // Enable/disable backups
8709    public void setBackupEnabled(boolean enable) {
8710        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8711                "setBackupEnabled");
8712
8713        Slog.i(TAG, "Backup enabled => " + enable);
8714
8715        long oldId = Binder.clearCallingIdentity();
8716        try {
8717            boolean wasEnabled = mEnabled;
8718            synchronized (this) {
8719                Settings.Secure.putInt(mContext.getContentResolver(),
8720                        Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
8721                mEnabled = enable;
8722            }
8723
8724            synchronized (mQueueLock) {
8725                if (enable && !wasEnabled && mProvisioned) {
8726                    // if we've just been enabled, start scheduling backup passes
8727                    KeyValueBackupJob.schedule(mContext);
8728                    scheduleNextFullBackupJob(0);
8729                } else if (!enable) {
8730                    // No longer enabled, so stop running backups
8731                    if (DEBUG) Slog.i(TAG, "Opting out of backup");
8732
8733                    KeyValueBackupJob.cancel(mContext);
8734
8735                    // This also constitutes an opt-out, so we wipe any data for
8736                    // this device from the backend.  We start that process with
8737                    // an alarm in order to guarantee wakelock states.
8738                    if (wasEnabled && mProvisioned) {
8739                        // NOTE: we currently flush every registered transport, not just
8740                        // the currently-active one.
8741                        HashSet<String> allTransports;
8742                        synchronized (mTransports) {
8743                            allTransports = new HashSet<String>(mTransports.keySet());
8744                        }
8745                        // build the set of transports for which we are posting an init
8746                        for (String transport : allTransports) {
8747                            recordInitPendingLocked(true, transport);
8748                        }
8749                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
8750                                mRunInitIntent);
8751                    }
8752                }
8753            }
8754        } finally {
8755            Binder.restoreCallingIdentity(oldId);
8756        }
8757    }
8758
8759    // Enable/disable automatic restore of app data at install time
8760    public void setAutoRestore(boolean doAutoRestore) {
8761        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8762                "setAutoRestore");
8763
8764        Slog.i(TAG, "Auto restore => " + doAutoRestore);
8765
8766        final long oldId = Binder.clearCallingIdentity();
8767        try {
8768            synchronized (this) {
8769                Settings.Secure.putInt(mContext.getContentResolver(),
8770                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
8771                mAutoRestore = doAutoRestore;
8772            }
8773        } finally {
8774            Binder.restoreCallingIdentity(oldId);
8775        }
8776    }
8777
8778    // Mark the backup service as having been provisioned
8779    public void setBackupProvisioned(boolean available) {
8780        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8781                "setBackupProvisioned");
8782        /*
8783         * This is now a no-op; provisioning is simply the device's own setup state.
8784         */
8785    }
8786
8787    // Report whether the backup mechanism is currently enabled
8788    public boolean isBackupEnabled() {
8789        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
8790        return mEnabled;    // no need to synchronize just to read it
8791    }
8792
8793    // Report the name of the currently active transport
8794    public String getCurrentTransport() {
8795        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8796                "getCurrentTransport");
8797        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
8798        return mCurrentTransport;
8799    }
8800
8801    // Report all known, available backup transports
8802    public String[] listAllTransports() {
8803        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
8804
8805        String[] list = null;
8806        ArrayList<String> known = new ArrayList<String>();
8807        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
8808            if (entry.getValue() != null) {
8809                known.add(entry.getKey());
8810            }
8811        }
8812
8813        if (known.size() > 0) {
8814            list = new String[known.size()];
8815            known.toArray(list);
8816        }
8817        return list;
8818    }
8819
8820    // Select which transport to use for the next backup operation.
8821    public String selectBackupTransport(String transport) {
8822        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8823                "selectBackupTransport");
8824
8825        synchronized (mTransports) {
8826            final long oldId = Binder.clearCallingIdentity();
8827            try {
8828                String prevTransport = mCurrentTransport;
8829                mCurrentTransport = transport;
8830                Settings.Secure.putString(mContext.getContentResolver(),
8831                        Settings.Secure.BACKUP_TRANSPORT, transport);
8832                Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
8833                        + " returning " + prevTransport);
8834                return prevTransport;
8835            } finally {
8836                Binder.restoreCallingIdentity(oldId);
8837            }
8838        }
8839    }
8840
8841    // Supply the configuration Intent for the given transport.  If the name is not one
8842    // of the available transports, or if the transport does not supply any configuration
8843    // UI, the method returns null.
8844    public Intent getConfigurationIntent(String transportName) {
8845        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8846                "getConfigurationIntent");
8847
8848        synchronized (mTransports) {
8849            final IBackupTransport transport = mTransports.get(transportName);
8850            if (transport != null) {
8851                try {
8852                    final Intent intent = transport.configurationIntent();
8853                    if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
8854                            + intent);
8855                    return intent;
8856                } catch (RemoteException e) {
8857                    /* fall through to return null */
8858                }
8859            }
8860        }
8861
8862        return null;
8863    }
8864
8865    // Supply the configuration summary string for the given transport.  If the name is
8866    // not one of the available transports, or if the transport does not supply any
8867    // summary / destination string, the method can return null.
8868    //
8869    // This string is used VERBATIM as the summary text of the relevant Settings item!
8870    public String getDestinationString(String transportName) {
8871        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8872                "getDestinationString");
8873
8874        synchronized (mTransports) {
8875            final IBackupTransport transport = mTransports.get(transportName);
8876            if (transport != null) {
8877                try {
8878                    final String text = transport.currentDestinationString();
8879                    if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() 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    // Supply the manage-data intent for the given transport.
8891    public Intent getDataManagementIntent(String transportName) {
8892        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8893                "getDataManagementIntent");
8894
8895        synchronized (mTransports) {
8896            final IBackupTransport transport = mTransports.get(transportName);
8897            if (transport != null) {
8898                try {
8899                    final Intent intent = transport.dataManagementIntent();
8900                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
8901                            + intent);
8902                    return intent;
8903                } catch (RemoteException e) {
8904                    /* fall through to return null */
8905                }
8906            }
8907        }
8908
8909        return null;
8910    }
8911
8912    // Supply the menu label for affordances that fire the manage-data intent
8913    // for the given transport.
8914    public String getDataManagementLabel(String transportName) {
8915        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8916                "getDataManagementLabel");
8917
8918        synchronized (mTransports) {
8919            final IBackupTransport transport = mTransports.get(transportName);
8920            if (transport != null) {
8921                try {
8922                    final String text = transport.dataManagementLabel();
8923                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
8924                    return text;
8925                } catch (RemoteException e) {
8926                    /* fall through to return null */
8927                }
8928            }
8929        }
8930
8931        return null;
8932    }
8933
8934    // Callback: a requested backup agent has been instantiated.  This should only
8935    // be called from the Activity Manager.
8936    public void agentConnected(String packageName, IBinder agentBinder) {
8937        synchronized(mAgentConnectLock) {
8938            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
8939                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
8940                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
8941                mConnectedAgent = agent;
8942                mConnecting = false;
8943            } else {
8944                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
8945                        + " claiming agent connected");
8946            }
8947            mAgentConnectLock.notifyAll();
8948        }
8949    }
8950
8951    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
8952    // If the agent failed to come up in the first place, the agentBinder argument
8953    // will be null.  This should only be called from the Activity Manager.
8954    public void agentDisconnected(String packageName) {
8955        // TODO: handle backup being interrupted
8956        synchronized(mAgentConnectLock) {
8957            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
8958                mConnectedAgent = null;
8959                mConnecting = false;
8960            } else {
8961                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
8962                        + " claiming agent disconnected");
8963            }
8964            mAgentConnectLock.notifyAll();
8965        }
8966    }
8967
8968    // An application being installed will need a restore pass, then the Package Manager
8969    // will need to be told when the restore is finished.
8970    public void restoreAtInstall(String packageName, int token) {
8971        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
8972            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
8973                    + " attemping install-time restore");
8974            return;
8975        }
8976
8977        boolean skip = false;
8978
8979        long restoreSet = getAvailableRestoreToken(packageName);
8980        if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
8981                + " token=" + Integer.toHexString(token)
8982                + " restoreSet=" + Long.toHexString(restoreSet));
8983        if (restoreSet == 0) {
8984            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
8985            skip = true;
8986        }
8987
8988        // Do we have a transport to fetch data for us?
8989        IBackupTransport transport = getTransport(mCurrentTransport);
8990        if (transport == null) {
8991            if (DEBUG) Slog.w(TAG, "No transport");
8992            skip = true;
8993        }
8994
8995        if (!mAutoRestore) {
8996            if (DEBUG) {
8997                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
8998            }
8999            skip = true;
9000        }
9001
9002        if (!skip) {
9003            try {
9004                // okay, we're going to attempt a restore of this package from this restore set.
9005                // The eventual message back into the Package Manager to run the post-install
9006                // steps for 'token' will be issued from the restore handling code.
9007
9008                // This can throw and so *must* happen before the wakelock is acquired
9009                String dirName = transport.transportDirName();
9010
9011                // We can use a synthetic PackageInfo here because:
9012                //   1. We know it's valid, since the Package Manager supplied the name
9013                //   2. Only the packageName field will be used by the restore code
9014                PackageInfo pkg = new PackageInfo();
9015                pkg.packageName = packageName;
9016
9017                mWakelock.acquire();
9018                if (MORE_DEBUG) {
9019                    Slog.d(TAG, "Restore at install of " + packageName);
9020                }
9021                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9022                msg.obj = new RestoreParams(transport, dirName, null,
9023                        restoreSet, pkg, token);
9024                mBackupHandler.sendMessage(msg);
9025            } catch (RemoteException e) {
9026                // Binding to the transport broke; back off and proceed with the installation.
9027                Slog.e(TAG, "Unable to contact transport");
9028                skip = true;
9029            }
9030        }
9031
9032        if (skip) {
9033            // Auto-restore disabled or no way to attempt a restore; just tell the Package
9034            // Manager to proceed with the post-install handling for this package.
9035            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
9036            try {
9037                mPackageManagerBinder.finishPackageInstall(token);
9038            } catch (RemoteException e) { /* can't happen */ }
9039        }
9040    }
9041
9042    // Hand off a restore session
9043    public IRestoreSession beginRestoreSession(String packageName, String transport) {
9044        if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
9045                + " transport=" + transport);
9046
9047        boolean needPermission = true;
9048        if (transport == null) {
9049            transport = mCurrentTransport;
9050
9051            if (packageName != null) {
9052                PackageInfo app = null;
9053                try {
9054                    app = mPackageManager.getPackageInfo(packageName, 0);
9055                } catch (NameNotFoundException nnf) {
9056                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9057                    throw new IllegalArgumentException("Package " + packageName + " not found");
9058                }
9059
9060                if (app.applicationInfo.uid == Binder.getCallingUid()) {
9061                    // So: using the current active transport, and the caller has asked
9062                    // that its own package will be restored.  In this narrow use case
9063                    // we do not require the caller to hold the permission.
9064                    needPermission = false;
9065                }
9066            }
9067        }
9068
9069        if (needPermission) {
9070            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9071                    "beginRestoreSession");
9072        } else {
9073            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
9074        }
9075
9076        synchronized(this) {
9077            if (mActiveRestoreSession != null) {
9078                Slog.d(TAG, "Restore session requested but one already active");
9079                return null;
9080            }
9081            mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
9082            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
9083        }
9084        return mActiveRestoreSession;
9085    }
9086
9087    void clearRestoreSession(ActiveRestoreSession currentSession) {
9088        synchronized(this) {
9089            if (currentSession != mActiveRestoreSession) {
9090                Slog.e(TAG, "ending non-current restore session");
9091            } else {
9092                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
9093                mActiveRestoreSession = null;
9094                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9095            }
9096        }
9097    }
9098
9099    // Note that a currently-active backup agent has notified us that it has
9100    // completed the given outstanding asynchronous backup/restore operation.
9101    public void opComplete(int token, long result) {
9102        if (MORE_DEBUG) {
9103            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
9104        }
9105        Operation op = null;
9106        synchronized (mCurrentOpLock) {
9107            op = mCurrentOperations.get(token);
9108            if (op != null) {
9109                op.state = OP_ACKNOWLEDGED;
9110            }
9111            mCurrentOpLock.notifyAll();
9112        }
9113
9114        // The completion callback, if any, is invoked on the handler
9115        if (op != null && op.callback != null) {
9116            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
9117            // NB: this cannot distinguish between results > 2 gig
9118            msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
9119            mBackupHandler.sendMessage(msg);
9120        }
9121    }
9122
9123    // ----- Restore session -----
9124
9125    class ActiveRestoreSession extends IRestoreSession.Stub {
9126        private static final String TAG = "RestoreSession";
9127
9128        private String mPackageName;
9129        private IBackupTransport mRestoreTransport = null;
9130        RestoreSet[] mRestoreSets = null;
9131        boolean mEnded = false;
9132        boolean mTimedOut = false;
9133
9134        ActiveRestoreSession(String packageName, String transport) {
9135            mPackageName = packageName;
9136            mRestoreTransport = getTransport(transport);
9137        }
9138
9139        public void markTimedOut() {
9140            mTimedOut = true;
9141        }
9142
9143        // --- Binder interface ---
9144        public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
9145            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9146                    "getAvailableRestoreSets");
9147            if (observer == null) {
9148                throw new IllegalArgumentException("Observer must not be null");
9149            }
9150
9151            if (mEnded) {
9152                throw new IllegalStateException("Restore session already ended");
9153            }
9154
9155            if (mTimedOut) {
9156                Slog.i(TAG, "Session already timed out");
9157                return -1;
9158            }
9159
9160            long oldId = Binder.clearCallingIdentity();
9161            try {
9162                if (mRestoreTransport == null) {
9163                    Slog.w(TAG, "Null transport getting restore sets");
9164                    return -1;
9165                }
9166
9167                // We know we're doing legit work now, so halt the timeout
9168                // until we're done.  It gets started again when the result
9169                // comes in.
9170                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9171
9172                // spin off the transport request to our service thread
9173                mWakelock.acquire();
9174                Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
9175                        new RestoreGetSetsParams(mRestoreTransport, this, observer));
9176                mBackupHandler.sendMessage(msg);
9177                return 0;
9178            } catch (Exception e) {
9179                Slog.e(TAG, "Error in getAvailableRestoreSets", e);
9180                return -1;
9181            } finally {
9182                Binder.restoreCallingIdentity(oldId);
9183            }
9184        }
9185
9186        public synchronized int restoreAll(long token, IRestoreObserver observer) {
9187            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9188                    "performRestore");
9189
9190            if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
9191                    + " observer=" + observer);
9192
9193            if (mEnded) {
9194                throw new IllegalStateException("Restore session already ended");
9195            }
9196
9197            if (mTimedOut) {
9198                Slog.i(TAG, "Session already timed out");
9199                return -1;
9200            }
9201
9202            if (mRestoreTransport == null || mRestoreSets == null) {
9203                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9204                return -1;
9205            }
9206
9207            if (mPackageName != null) {
9208                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9209                return -1;
9210            }
9211
9212            String dirName;
9213            try {
9214                dirName = mRestoreTransport.transportDirName();
9215            } catch (RemoteException e) {
9216                // Transport went AWOL; fail.
9217                Slog.e(TAG, "Unable to contact transport for restore");
9218                return -1;
9219            }
9220
9221            synchronized (mQueueLock) {
9222                for (int i = 0; i < mRestoreSets.length; i++) {
9223                    if (token == mRestoreSets[i].token) {
9224                        // Real work, so stop the session timeout until we finalize the restore
9225                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9226
9227                        long oldId = Binder.clearCallingIdentity();
9228                        mWakelock.acquire();
9229                        if (MORE_DEBUG) {
9230                            Slog.d(TAG, "restoreAll() kicking off");
9231                        }
9232                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9233                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
9234                                observer, token);
9235                        mBackupHandler.sendMessage(msg);
9236                        Binder.restoreCallingIdentity(oldId);
9237                        return 0;
9238                    }
9239                }
9240            }
9241
9242            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9243            return -1;
9244        }
9245
9246        // Restores of more than a single package are treated as 'system' restores
9247        public synchronized int restoreSome(long token, IRestoreObserver observer,
9248                String[] packages) {
9249            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9250                    "performRestore");
9251
9252            if (DEBUG) {
9253                StringBuilder b = new StringBuilder(128);
9254                b.append("restoreSome token=");
9255                b.append(Long.toHexString(token));
9256                b.append(" observer=");
9257                b.append(observer.toString());
9258                b.append(" packages=");
9259                if (packages == null) {
9260                    b.append("null");
9261                } else {
9262                    b.append('{');
9263                    boolean first = true;
9264                    for (String s : packages) {
9265                        if (!first) {
9266                            b.append(", ");
9267                        } else first = false;
9268                        b.append(s);
9269                    }
9270                    b.append('}');
9271                }
9272                Slog.d(TAG, b.toString());
9273            }
9274
9275            if (mEnded) {
9276                throw new IllegalStateException("Restore session already ended");
9277            }
9278
9279            if (mTimedOut) {
9280                Slog.i(TAG, "Session already timed out");
9281                return -1;
9282            }
9283
9284            if (mRestoreTransport == null || mRestoreSets == null) {
9285                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9286                return -1;
9287            }
9288
9289            if (mPackageName != null) {
9290                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9291                return -1;
9292            }
9293
9294            String dirName;
9295            try {
9296                dirName = mRestoreTransport.transportDirName();
9297            } catch (RemoteException e) {
9298                // Transport went AWOL; fail.
9299                Slog.e(TAG, "Unable to contact transport for restore");
9300                return -1;
9301            }
9302
9303            synchronized (mQueueLock) {
9304                for (int i = 0; i < mRestoreSets.length; i++) {
9305                    if (token == mRestoreSets[i].token) {
9306                        // Stop the session timeout until we finalize the restore
9307                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9308
9309                        long oldId = Binder.clearCallingIdentity();
9310                        mWakelock.acquire();
9311                        if (MORE_DEBUG) {
9312                            Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
9313                        }
9314                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9315                        msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
9316                                packages, packages.length > 1);
9317                        mBackupHandler.sendMessage(msg);
9318                        Binder.restoreCallingIdentity(oldId);
9319                        return 0;
9320                    }
9321                }
9322            }
9323
9324            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9325            return -1;
9326        }
9327
9328        public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
9329            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
9330
9331            if (mEnded) {
9332                throw new IllegalStateException("Restore session already ended");
9333            }
9334
9335            if (mTimedOut) {
9336                Slog.i(TAG, "Session already timed out");
9337                return -1;
9338            }
9339
9340            if (mPackageName != null) {
9341                if (! mPackageName.equals(packageName)) {
9342                    Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
9343                            + " on session for package " + mPackageName);
9344                    return -1;
9345                }
9346            }
9347
9348            PackageInfo app = null;
9349            try {
9350                app = mPackageManager.getPackageInfo(packageName, 0);
9351            } catch (NameNotFoundException nnf) {
9352                Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9353                return -1;
9354            }
9355
9356            // If the caller is not privileged and is not coming from the target
9357            // app's uid, throw a permission exception back to the caller.
9358            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
9359                    Binder.getCallingPid(), Binder.getCallingUid());
9360            if ((perm == PackageManager.PERMISSION_DENIED) &&
9361                    (app.applicationInfo.uid != Binder.getCallingUid())) {
9362                Slog.w(TAG, "restorePackage: bad packageName=" + packageName
9363                        + " or calling uid=" + Binder.getCallingUid());
9364                throw new SecurityException("No permission to restore other packages");
9365            }
9366
9367            // So far so good; we're allowed to try to restore this package.  Now
9368            // check whether there is data for it in the current dataset, falling back
9369            // to the ancestral dataset if not.
9370            long token = getAvailableRestoreToken(packageName);
9371            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName
9372                    + " token=" + Long.toHexString(token));
9373
9374            // If we didn't come up with a place to look -- no ancestral dataset and
9375            // the app has never been backed up from this device -- there's nothing
9376            // to do but return failure.
9377            if (token == 0) {
9378                if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
9379                return -1;
9380            }
9381
9382            String dirName;
9383            try {
9384                dirName = mRestoreTransport.transportDirName();
9385            } catch (RemoteException e) {
9386                // Transport went AWOL; fail.
9387                Slog.e(TAG, "Unable to contact transport for restore");
9388                return -1;
9389            }
9390
9391            // Stop the session timeout until we finalize the restore
9392            mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9393
9394            // Ready to go:  enqueue the restore request and claim success
9395            long oldId = Binder.clearCallingIdentity();
9396            mWakelock.acquire();
9397            if (MORE_DEBUG) {
9398                Slog.d(TAG, "restorePackage() : " + packageName);
9399            }
9400            Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9401            msg.obj = new RestoreParams(mRestoreTransport, dirName,
9402                    observer, token, app, 0);
9403            mBackupHandler.sendMessage(msg);
9404            Binder.restoreCallingIdentity(oldId);
9405            return 0;
9406        }
9407
9408        // Posted to the handler to tear down a restore session in a cleanly synchronized way
9409        class EndRestoreRunnable implements Runnable {
9410            BackupManagerService mBackupManager;
9411            ActiveRestoreSession mSession;
9412
9413            EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
9414                mBackupManager = manager;
9415                mSession = session;
9416            }
9417
9418            public void run() {
9419                // clean up the session's bookkeeping
9420                synchronized (mSession) {
9421                    try {
9422                        if (mSession.mRestoreTransport != null) {
9423                            mSession.mRestoreTransport.finishRestore();
9424                        }
9425                    } catch (Exception e) {
9426                        Slog.e(TAG, "Error in finishRestore", e);
9427                    } finally {
9428                        mSession.mRestoreTransport = null;
9429                        mSession.mEnded = true;
9430                    }
9431                }
9432
9433                // clean up the BackupManagerImpl side of the bookkeeping
9434                // and cancel any pending timeout message
9435                mBackupManager.clearRestoreSession(mSession);
9436            }
9437        }
9438
9439        public synchronized void endRestoreSession() {
9440            if (DEBUG) Slog.d(TAG, "endRestoreSession");
9441
9442            if (mTimedOut) {
9443                Slog.i(TAG, "Session already timed out");
9444                return;
9445            }
9446
9447            if (mEnded) {
9448                throw new IllegalStateException("Restore session already ended");
9449            }
9450
9451            mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
9452        }
9453    }
9454
9455    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
9456        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
9457
9458        long identityToken = Binder.clearCallingIdentity();
9459        try {
9460            if (args != null) {
9461                for (String arg : args) {
9462                    if ("-h".equals(arg)) {
9463                        pw.println("'dumpsys backup' optional arguments:");
9464                        pw.println("  -h       : this help text");
9465                        pw.println("  a[gents] : dump information about defined backup agents");
9466                        return;
9467                    } else if ("agents".startsWith(arg)) {
9468                        dumpAgents(pw);
9469                        return;
9470                    }
9471                }
9472            }
9473            dumpInternal(pw);
9474        } finally {
9475            Binder.restoreCallingIdentity(identityToken);
9476        }
9477    }
9478
9479    private void dumpAgents(PrintWriter pw) {
9480        List<PackageInfo> agentPackages = allAgentPackages();
9481        pw.println("Defined backup agents:");
9482        for (PackageInfo pkg : agentPackages) {
9483            pw.print("  ");
9484            pw.print(pkg.packageName); pw.println(':');
9485            pw.print("      "); pw.println(pkg.applicationInfo.backupAgentName);
9486        }
9487    }
9488
9489    private void dumpInternal(PrintWriter pw) {
9490        synchronized (mQueueLock) {
9491            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
9492                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
9493                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
9494            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
9495            if (mBackupRunning) pw.println("Backup currently running");
9496            pw.println("Last backup pass started: " + mLastBackupPass
9497                    + " (now = " + System.currentTimeMillis() + ')');
9498            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
9499
9500            pw.println("Available transports:");
9501            final String[] transports = listAllTransports();
9502            if (transports != null) {
9503                for (String t : listAllTransports()) {
9504                    pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
9505                    try {
9506                        IBackupTransport transport = getTransport(t);
9507                        File dir = new File(mBaseStateDir, transport.transportDirName());
9508                        pw.println("       destination: " + transport.currentDestinationString());
9509                        pw.println("       intent: " + transport.configurationIntent());
9510                        for (File f : dir.listFiles()) {
9511                            pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
9512                        }
9513                    } catch (Exception e) {
9514                        Slog.e(TAG, "Error in transport", e);
9515                        pw.println("        Error: " + e);
9516                    }
9517                }
9518            }
9519
9520            pw.println("Pending init: " + mPendingInits.size());
9521            for (String s : mPendingInits) {
9522                pw.println("    " + s);
9523            }
9524
9525            if (DEBUG_BACKUP_TRACE) {
9526                synchronized (mBackupTrace) {
9527                    if (!mBackupTrace.isEmpty()) {
9528                        pw.println("Most recent backup trace:");
9529                        for (String s : mBackupTrace) {
9530                            pw.println("   " + s);
9531                        }
9532                    }
9533                }
9534            }
9535
9536            int N = mBackupParticipants.size();
9537            pw.println("Participants:");
9538            for (int i=0; i<N; i++) {
9539                int uid = mBackupParticipants.keyAt(i);
9540                pw.print("  uid: ");
9541                pw.println(uid);
9542                HashSet<String> participants = mBackupParticipants.valueAt(i);
9543                for (String app: participants) {
9544                    pw.println("    " + app);
9545                }
9546            }
9547
9548            pw.println("Ancestral packages: "
9549                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
9550            if (mAncestralPackages != null) {
9551                for (String pkg : mAncestralPackages) {
9552                    pw.println("    " + pkg);
9553                }
9554            }
9555
9556            pw.println("Ever backed up: " + mEverStoredApps.size());
9557            for (String pkg : mEverStoredApps) {
9558                pw.println("    " + pkg);
9559            }
9560
9561            pw.println("Pending key/value backup: " + mPendingBackups.size());
9562            for (BackupRequest req : mPendingBackups.values()) {
9563                pw.println("    " + req);
9564            }
9565
9566            pw.println("Full backup queue:" + mFullBackupQueue.size());
9567            for (FullBackupEntry entry : mFullBackupQueue) {
9568                pw.print("    "); pw.print(entry.lastBackup);
9569                pw.print(" : "); pw.println(entry.packageName);
9570            }
9571        }
9572    }
9573}
9574