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