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