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