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