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