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