BackupManagerService.java revision 339b53a8e678f18da906906f5f32ed57772102bb
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            try {
3172                int size = (int) mBackupDataName.length();
3173                if (size > 0) {
3174                    if (mStatus == BackupTransport.TRANSPORT_OK) {
3175                        backupData = ParcelFileDescriptor.open(mBackupDataName,
3176                                ParcelFileDescriptor.MODE_READ_ONLY);
3177                        addBackupTrace("sending data to transport");
3178                        int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
3179                        mStatus = mTransport.performBackup(mCurrentPackage, backupData, flags);
3180                    }
3181
3182                    // TODO - We call finishBackup() for each application backed up, because
3183                    // we need to know now whether it succeeded or failed.  Instead, we should
3184                    // hold off on finishBackup() until the end, which implies holding off on
3185                    // renaming *all* the output state files (see below) until that happens.
3186
3187                    addBackupTrace("data delivered: " + mStatus);
3188                    if (mStatus == BackupTransport.TRANSPORT_OK) {
3189                        addBackupTrace("finishing op on transport");
3190                        mStatus = mTransport.finishBackup();
3191                        addBackupTrace("finished: " + mStatus);
3192                    } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3193                        addBackupTrace("transport rejected package");
3194                    }
3195                } else {
3196                    if (MORE_DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
3197                    addBackupTrace("no data to send");
3198                }
3199
3200                if (mStatus == BackupTransport.TRANSPORT_OK) {
3201                    // After successful transport, delete the now-stale data
3202                    // and juggle the files so that next time we supply the agent
3203                    // with the new state file it just created.
3204                    mBackupDataName.delete();
3205                    mNewStateName.renameTo(mSavedStateName);
3206                    sendBackupOnResult(mObserver, pkgName, BackupManager.SUCCESS);
3207                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
3208                    logBackupComplete(pkgName);
3209                } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3210                    // The transport has rejected backup of this specific package.  Roll it
3211                    // back but proceed with running the rest of the queue.
3212                    mBackupDataName.delete();
3213                    mNewStateName.delete();
3214                    sendBackupOnResult(mObserver, pkgName,
3215                            BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
3216                    EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
3217                } else {
3218                    // Actual transport-level failure to communicate the data to the backend
3219                    sendBackupOnResult(mObserver, pkgName, BackupManager.ERROR_TRANSPORT_ABORTED);
3220                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
3221                }
3222            } catch (Exception e) {
3223                sendBackupOnResult(mObserver, pkgName, BackupManager.ERROR_TRANSPORT_ABORTED);
3224                Slog.e(TAG, "Transport error backing up " + pkgName, e);
3225                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
3226                mStatus = BackupTransport.TRANSPORT_ERROR;
3227            } finally {
3228                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
3229            }
3230
3231            final BackupState nextState;
3232            if (mStatus == BackupTransport.TRANSPORT_OK
3233                    || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3234                // Success or single-package rejection.  Proceed with the next app if any,
3235                // otherwise we're done.
3236                nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
3237            } else {
3238                // Any other error here indicates a transport-level failure.  That means
3239                // we need to halt everything and reschedule everything for next time.
3240                revertAndEndBackup();
3241                nextState = BackupState.FINAL;
3242            }
3243
3244            executeNextState(nextState);
3245        }
3246
3247        @Override
3248        public void handleTimeout() {
3249            // Whoops, the current agent timed out running doBackup().  Tidy up and restage
3250            // it for the next time we run a backup pass.
3251            // !!! TODO: keep track of failure counts per agent, and blacklist those which
3252            // fail repeatedly (i.e. have proved themselves to be buggy).
3253            Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
3254            EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
3255                    "timeout");
3256            addBackupTrace("timeout of " + mCurrentPackage.packageName);
3257            agentErrorCleanup();
3258            dataChangedImpl(mCurrentPackage.packageName);
3259        }
3260
3261        void revertAndEndBackup() {
3262            if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
3263            addBackupTrace("transport error; reverting");
3264
3265            // We want to reset the backup schedule based on whatever the transport suggests
3266            // by way of retry/backoff time.
3267            long delay;
3268            try {
3269                delay = mTransport.requestBackupTime();
3270            } catch (Exception e) {
3271                Slog.w(TAG, "Unable to contact transport for recommended backoff");
3272                delay = 0;  // use the scheduler's default
3273            }
3274            KeyValueBackupJob.schedule(mContext, delay);
3275
3276            for (BackupRequest request : mOriginalQueue) {
3277                dataChangedImpl(request.packageName);
3278            }
3279
3280        }
3281
3282        void agentErrorCleanup() {
3283            mBackupDataName.delete();
3284            mNewStateName.delete();
3285            clearAgentState();
3286
3287            executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
3288        }
3289
3290        // Cleanup common to both success and failure cases
3291        void clearAgentState() {
3292            try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {}
3293            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
3294            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
3295            synchronized (mCurrentOpLock) {
3296                // Current-operation callback handling requires the validity of these various
3297                // bits of internal state as an invariant of the operation still being live.
3298                // This means we make sure to clear all of the state in unison inside the lock.
3299                mCurrentOperations.clear();
3300                mSavedState = mBackupData = mNewState = null;
3301            }
3302
3303            // If this was a pseudopackage there's no associated Activity Manager state
3304            if (mCurrentPackage.applicationInfo != null) {
3305                addBackupTrace("unbinding " + mCurrentPackage.packageName);
3306                try {  // unbind even on timeout, just in case
3307                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
3308                } catch (RemoteException e) { /* can't happen; activity manager is local */ }
3309            }
3310        }
3311
3312        void executeNextState(BackupState nextState) {
3313            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
3314                    + this + " nextState=" + nextState);
3315            addBackupTrace("executeNextState => " + nextState);
3316            mCurrentState = nextState;
3317            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
3318            mBackupHandler.sendMessage(msg);
3319        }
3320    }
3321
3322
3323    // ----- Full backup/restore to a file/socket -----
3324
3325    class FullBackupObbConnection implements ServiceConnection {
3326        volatile IObbBackupService mService;
3327
3328        FullBackupObbConnection() {
3329            mService = null;
3330        }
3331
3332        public void establish() {
3333            if (MORE_DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this);
3334            Intent obbIntent = new Intent().setComponent(new ComponentName(
3335                    "com.android.sharedstoragebackup",
3336                    "com.android.sharedstoragebackup.ObbBackupService"));
3337            BackupManagerService.this.mContext.bindService(
3338                    obbIntent, this, Context.BIND_AUTO_CREATE);
3339        }
3340
3341        public void tearDown() {
3342            BackupManagerService.this.mContext.unbindService(this);
3343        }
3344
3345        public boolean backupObbs(PackageInfo pkg, OutputStream out) {
3346            boolean success = false;
3347            waitForConnection();
3348
3349            ParcelFileDescriptor[] pipes = null;
3350            try {
3351                pipes = ParcelFileDescriptor.createPipe();
3352                int token = generateToken();
3353                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
3354                mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
3355                routeSocketDataToOutput(pipes[0], out);
3356                success = waitUntilOperationComplete(token);
3357            } catch (Exception e) {
3358                Slog.w(TAG, "Unable to back up OBBs for " + pkg, e);
3359            } finally {
3360                try {
3361                    out.flush();
3362                    if (pipes != null) {
3363                        if (pipes[0] != null) pipes[0].close();
3364                        if (pipes[1] != null) pipes[1].close();
3365                    }
3366                } catch (IOException e) {
3367                    Slog.w(TAG, "I/O error closing down OBB backup", e);
3368                }
3369            }
3370            return success;
3371        }
3372
3373        public void restoreObbFile(String pkgName, ParcelFileDescriptor data,
3374                long fileSize, int type, String path, long mode, long mtime,
3375                int token, IBackupManager callbackBinder) {
3376            waitForConnection();
3377
3378            try {
3379                mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime,
3380                        token, callbackBinder);
3381            } catch (Exception e) {
3382                Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e);
3383            }
3384        }
3385
3386        private void waitForConnection() {
3387            synchronized (this) {
3388                while (mService == null) {
3389                    if (MORE_DEBUG) Slog.i(TAG, "...waiting for OBB service binding...");
3390                    try {
3391                        this.wait();
3392                    } catch (InterruptedException e) { /* never interrupted */ }
3393                }
3394                if (MORE_DEBUG) Slog.i(TAG, "Connected to OBB service; continuing");
3395            }
3396        }
3397
3398        @Override
3399        public void onServiceConnected(ComponentName name, IBinder service) {
3400            synchronized (this) {
3401                mService = IObbBackupService.Stub.asInterface(service);
3402                if (MORE_DEBUG) Slog.i(TAG, "OBB service connection " + mService
3403                        + " connected on " + this);
3404                this.notifyAll();
3405            }
3406        }
3407
3408        @Override
3409        public void onServiceDisconnected(ComponentName name) {
3410            synchronized (this) {
3411                mService = null;
3412                if (MORE_DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this);
3413                this.notifyAll();
3414            }
3415        }
3416
3417    }
3418
3419    private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
3420            throws IOException {
3421        FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
3422        DataInputStream in = new DataInputStream(raw);
3423
3424        byte[] buffer = new byte[32 * 1024];
3425        int chunkTotal;
3426        while ((chunkTotal = in.readInt()) > 0) {
3427            while (chunkTotal > 0) {
3428                int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
3429                int nRead = in.read(buffer, 0, toRead);
3430                out.write(buffer, 0, nRead);
3431                chunkTotal -= nRead;
3432            }
3433        }
3434    }
3435
3436    void tearDownAgentAndKill(ApplicationInfo app) {
3437        try {
3438            // unbind and tidy up even on timeout or failure, just in case
3439            mActivityManager.unbindBackupAgent(app);
3440
3441            // The agent was running with a stub Application object, so shut it down.
3442            // !!! We hardcode the confirmation UI's package name here rather than use a
3443            //     manifest flag!  TODO something less direct.
3444            if (app.uid != Process.SYSTEM_UID
3445                    && !app.packageName.equals("com.android.backupconfirm")
3446                    && app.uid != Process.PHONE_UID) {
3447                if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
3448                mActivityManager.killApplicationProcess(app.processName, app.uid);
3449            } else {
3450                if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
3451            }
3452        } catch (RemoteException e) {
3453            Slog.d(TAG, "Lost app trying to shut down");
3454        }
3455    }
3456
3457    // Core logic for performing one package's full backup, gathering the tarball from the
3458    // application and emitting it to the designated OutputStream.
3459
3460    // Callout from the engine to an interested participant that might need to communicate
3461    // with the agent prior to asking it to move data
3462    interface FullBackupPreflight {
3463        /**
3464         * Perform the preflight operation necessary for the given package.
3465         * @param pkg The name of the package being proposed for full-data backup
3466         * @param agent Live BackupAgent binding to the target app's agent
3467         * @return BackupTransport.TRANSPORT_OK to proceed with the backup operation,
3468         *         or one of the other BackupTransport.* error codes as appropriate
3469         */
3470        int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
3471
3472        long expectedSize();
3473    };
3474
3475    class FullBackupEngine {
3476        OutputStream mOutput;
3477        FullBackupPreflight mPreflightHook;
3478        IFullBackupRestoreObserver mObserver;
3479        File mFilesDir;
3480        File mManifestFile;
3481        File mMetadataFile;
3482        boolean mIncludeApks;
3483
3484        class FullBackupRunner implements Runnable {
3485            PackageInfo mPackage;
3486            byte[] mWidgetData;
3487            IBackupAgent mAgent;
3488            ParcelFileDescriptor mPipe;
3489            int mToken;
3490            boolean mSendApk;
3491            boolean mWriteManifest;
3492
3493            FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
3494                    int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
3495                            throws IOException {
3496                mPackage = pack;
3497                mWidgetData = widgetData;
3498                mAgent = agent;
3499                mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
3500                mToken = token;
3501                mSendApk = sendApk;
3502                mWriteManifest = writeManifest;
3503            }
3504
3505            @Override
3506            public void run() {
3507                try {
3508                    FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
3509
3510                    if (mWriteManifest) {
3511                        final boolean writeWidgetData = mWidgetData != null;
3512                        if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
3513                        writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
3514                        FullBackup.backupToTar(mPackage.packageName, null, null,
3515                                mFilesDir.getAbsolutePath(),
3516                                mManifestFile.getAbsolutePath(),
3517                                output);
3518                        mManifestFile.delete();
3519
3520                        // We only need to write a metadata file if we have widget data to stash
3521                        if (writeWidgetData) {
3522                            writeMetadata(mPackage, mMetadataFile, mWidgetData);
3523                            FullBackup.backupToTar(mPackage.packageName, null, null,
3524                                    mFilesDir.getAbsolutePath(),
3525                                    mMetadataFile.getAbsolutePath(),
3526                                    output);
3527                            mMetadataFile.delete();
3528                        }
3529                    }
3530
3531                    if (mSendApk) {
3532                        writeApkToBackup(mPackage, output);
3533                    }
3534
3535                    if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
3536                    prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null);
3537                    mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
3538                } catch (IOException e) {
3539                    Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
3540                } catch (RemoteException e) {
3541                    Slog.e(TAG, "Remote agent vanished during full backup of "
3542                            + mPackage.packageName);
3543                } finally {
3544                    try {
3545                        mPipe.close();
3546                    } catch (IOException e) {}
3547                }
3548            }
3549        }
3550
3551        FullBackupEngine(OutputStream output, String packageName, FullBackupPreflight preflightHook,
3552                boolean alsoApks) {
3553            mOutput = output;
3554            mPreflightHook = preflightHook;
3555            mIncludeApks = alsoApks;
3556            mFilesDir = new File("/data/system");
3557            mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
3558            mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
3559        }
3560
3561        public int backupOnePackage(PackageInfo pkg) throws RemoteException {
3562            int result = BackupTransport.TRANSPORT_OK;
3563            Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
3564
3565            IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
3566                    IApplicationThread.BACKUP_MODE_FULL);
3567            if (agent != null) {
3568                ParcelFileDescriptor[] pipes = null;
3569                try {
3570                    // Call the preflight hook, if any
3571                    if (mPreflightHook != null) {
3572                        result = mPreflightHook.preflightFullBackup(pkg, agent);
3573                        if (MORE_DEBUG) {
3574                            Slog.v(TAG, "preflight returned " + result);
3575                        }
3576                    }
3577
3578                    // If we're still good to go after preflighting, start moving data
3579                    if (result == BackupTransport.TRANSPORT_OK) {
3580                        pipes = ParcelFileDescriptor.createPipe();
3581
3582                        ApplicationInfo app = pkg.applicationInfo;
3583                        final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
3584                        final boolean sendApk = mIncludeApks
3585                                && !isSharedStorage
3586                                && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
3587                                && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
3588                                (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
3589
3590                        // TODO: http://b/22388012
3591                        byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
3592                                UserHandle.USER_SYSTEM);
3593
3594                        final int token = generateToken();
3595                        FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
3596                                token, sendApk, !isSharedStorage, widgetBlob);
3597                        pipes[1].close();   // the runner has dup'd it
3598                        pipes[1] = null;
3599                        Thread t = new Thread(runner, "app-data-runner");
3600                        t.start();
3601
3602                        // Now pull data from the app and stuff it into the output
3603                        try {
3604                            routeSocketDataToOutput(pipes[0], mOutput);
3605                        } catch (IOException e) {
3606                            Slog.i(TAG, "Caught exception reading from agent", e);
3607                            result = BackupTransport.AGENT_ERROR;
3608                        }
3609
3610                        if (!waitUntilOperationComplete(token)) {
3611                            Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
3612                            result = BackupTransport.AGENT_ERROR;
3613                        } else {
3614                            if (MORE_DEBUG) {
3615                                Slog.d(TAG, "Full package backup success: " + pkg.packageName);
3616                            }
3617                        }
3618                    }
3619                } catch (IOException e) {
3620                    Slog.e(TAG, "Error backing up " + pkg.packageName, e);
3621                    result = BackupTransport.AGENT_ERROR;
3622                } finally {
3623                    try {
3624                        // flush after every package
3625                        mOutput.flush();
3626                        if (pipes != null) {
3627                            if (pipes[0] != null) pipes[0].close();
3628                            if (pipes[1] != null) pipes[1].close();
3629                        }
3630                    } catch (IOException e) {
3631                        Slog.w(TAG, "Error bringing down backup stack");
3632                        result = BackupTransport.TRANSPORT_ERROR;
3633                    }
3634                }
3635            } else {
3636                Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
3637                result = BackupTransport.AGENT_ERROR;
3638            }
3639            tearDown(pkg);
3640            return result;
3641        }
3642
3643        private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
3644            // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
3645            // TODO: handle backing up split APKs
3646            final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
3647            final String apkDir = new File(appSourceDir).getParent();
3648            FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
3649                    apkDir, appSourceDir, output);
3650
3651            // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM
3652            // doesn't have access to external storage.
3653
3654            // Save associated .obb content if it exists and we did save the apk
3655            // check for .obb and save those too
3656            // TODO: http://b/22388012
3657            final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_SYSTEM);
3658            final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0];
3659            if (obbDir != null) {
3660                if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
3661                File[] obbFiles = obbDir.listFiles();
3662                if (obbFiles != null) {
3663                    final String obbDirName = obbDir.getAbsolutePath();
3664                    for (File obb : obbFiles) {
3665                        FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
3666                                obbDirName, obb.getAbsolutePath(), output);
3667                    }
3668                }
3669            }
3670        }
3671
3672        private void writeAppManifest(PackageInfo pkg, File manifestFile,
3673                boolean withApk, boolean withWidgets) throws IOException {
3674            // Manifest format. All data are strings ending in LF:
3675            //     BACKUP_MANIFEST_VERSION, currently 1
3676            //
3677            // Version 1:
3678            //     package name
3679            //     package's versionCode
3680            //     platform versionCode
3681            //     getInstallerPackageName() for this package (maybe empty)
3682            //     boolean: "1" if archive includes .apk; any other string means not
3683            //     number of signatures == N
3684            // N*:    signature byte array in ascii format per Signature.toCharsString()
3685            StringBuilder builder = new StringBuilder(4096);
3686            StringBuilderPrinter printer = new StringBuilderPrinter(builder);
3687
3688            printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
3689            printer.println(pkg.packageName);
3690            printer.println(Integer.toString(pkg.versionCode));
3691            printer.println(Integer.toString(Build.VERSION.SDK_INT));
3692
3693            String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
3694            printer.println((installerName != null) ? installerName : "");
3695
3696            printer.println(withApk ? "1" : "0");
3697            if (pkg.signatures == null) {
3698                printer.println("0");
3699            } else {
3700                printer.println(Integer.toString(pkg.signatures.length));
3701                for (Signature sig : pkg.signatures) {
3702                    printer.println(sig.toCharsString());
3703                }
3704            }
3705
3706            FileOutputStream outstream = new FileOutputStream(manifestFile);
3707            outstream.write(builder.toString().getBytes());
3708            outstream.close();
3709
3710            // We want the manifest block in the archive stream to be idempotent:
3711            // each time we generate a backup stream for the app, we want the manifest
3712            // block to be identical.  The underlying tar mechanism sees it as a file,
3713            // though, and will propagate its mtime, causing the tar header to vary.
3714            // Avoid this problem by pinning the mtime to zero.
3715            manifestFile.setLastModified(0);
3716        }
3717
3718        // Widget metadata format. All header entries are strings ending in LF:
3719        //
3720        // Version 1 header:
3721        //     BACKUP_METADATA_VERSION, currently "1"
3722        //     package name
3723        //
3724        // File data (all integers are binary in network byte order)
3725        // *N: 4 : integer token identifying which metadata blob
3726        //     4 : integer size of this blob = N
3727        //     N : raw bytes of this metadata blob
3728        //
3729        // Currently understood blobs (always in network byte order):
3730        //
3731        //     widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
3732        //
3733        // Unrecognized blobs are *ignored*, not errors.
3734        private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
3735                throws IOException {
3736            StringBuilder b = new StringBuilder(512);
3737            StringBuilderPrinter printer = new StringBuilderPrinter(b);
3738            printer.println(Integer.toString(BACKUP_METADATA_VERSION));
3739            printer.println(pkg.packageName);
3740
3741            FileOutputStream fout = new FileOutputStream(destination);
3742            BufferedOutputStream bout = new BufferedOutputStream(fout);
3743            DataOutputStream out = new DataOutputStream(bout);
3744            bout.write(b.toString().getBytes());    // bypassing DataOutputStream
3745
3746            if (widgetData != null && widgetData.length > 0) {
3747                out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
3748                out.writeInt(widgetData.length);
3749                out.write(widgetData);
3750            }
3751            bout.flush();
3752            out.close();
3753
3754            // As with the manifest file, guarantee idempotence of the archive metadata
3755            // for the widget block by using a fixed mtime on the transient file.
3756            destination.setLastModified(0);
3757        }
3758
3759        private void tearDown(PackageInfo pkg) {
3760            if (pkg != null) {
3761                final ApplicationInfo app = pkg.applicationInfo;
3762                if (app != null) {
3763                    tearDownAgentAndKill(app);
3764                }
3765            }
3766        }
3767    }
3768
3769    // Generic driver skeleton for full backup operations
3770    abstract class FullBackupTask implements Runnable {
3771        IFullBackupRestoreObserver mObserver;
3772
3773        FullBackupTask(IFullBackupRestoreObserver observer) {
3774            mObserver = observer;
3775        }
3776
3777        // wrappers for observer use
3778        final void sendStartBackup() {
3779            if (mObserver != null) {
3780                try {
3781                    mObserver.onStartBackup();
3782                } catch (RemoteException e) {
3783                    Slog.w(TAG, "full backup observer went away: startBackup");
3784                    mObserver = null;
3785                }
3786            }
3787        }
3788
3789        final void sendOnBackupPackage(String name) {
3790            if (mObserver != null) {
3791                try {
3792                    // TODO: use a more user-friendly name string
3793                    mObserver.onBackupPackage(name);
3794                } catch (RemoteException e) {
3795                    Slog.w(TAG, "full backup observer went away: backupPackage");
3796                    mObserver = null;
3797                }
3798            }
3799        }
3800
3801        final void sendEndBackup() {
3802            if (mObserver != null) {
3803                try {
3804                    mObserver.onEndBackup();
3805                } catch (RemoteException e) {
3806                    Slog.w(TAG, "full backup observer went away: endBackup");
3807                    mObserver = null;
3808                }
3809            }
3810        }
3811    }
3812
3813    boolean deviceIsEncrypted() {
3814        try {
3815            return mMountService.getEncryptionState()
3816                     != IMountService.ENCRYPTION_STATE_NONE
3817                && mMountService.getPasswordType()
3818                     != StorageManager.CRYPT_TYPE_DEFAULT;
3819        } catch (Exception e) {
3820            // If we can't talk to the mount service we have a serious problem; fail
3821            // "secure" i.e. assuming that the device is encrypted.
3822            Slog.e(TAG, "Unable to communicate with mount service: " + e.getMessage());
3823            return true;
3824        }
3825    }
3826
3827    // Full backup task variant used for adb backup
3828    class PerformAdbBackupTask extends FullBackupTask {
3829        FullBackupEngine mBackupEngine;
3830        final AtomicBoolean mLatch;
3831
3832        ParcelFileDescriptor mOutputFile;
3833        DeflaterOutputStream mDeflater;
3834        boolean mIncludeApks;
3835        boolean mIncludeObbs;
3836        boolean mIncludeShared;
3837        boolean mDoWidgets;
3838        boolean mAllApps;
3839        boolean mIncludeSystem;
3840        boolean mCompress;
3841        ArrayList<String> mPackages;
3842        String mCurrentPassword;
3843        String mEncryptPassword;
3844
3845        PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
3846                boolean includeApks, boolean includeObbs, boolean includeShared,
3847                boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
3848                boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch) {
3849            super(observer);
3850            mLatch = latch;
3851
3852            mOutputFile = fd;
3853            mIncludeApks = includeApks;
3854            mIncludeObbs = includeObbs;
3855            mIncludeShared = includeShared;
3856            mDoWidgets = doWidgets;
3857            mAllApps = doAllApps;
3858            mIncludeSystem = doSystem;
3859            mPackages = (packages == null)
3860                    ? new ArrayList<String>()
3861                    : new ArrayList<String>(Arrays.asList(packages));
3862            mCurrentPassword = curPassword;
3863            // when backing up, if there is a current backup password, we require that
3864            // the user use a nonempty encryption password as well.  if one is supplied
3865            // in the UI we use that, but if the UI was left empty we fall back to the
3866            // current backup password (which was supplied by the user as well).
3867            if (encryptPassword == null || "".equals(encryptPassword)) {
3868                mEncryptPassword = curPassword;
3869            } else {
3870                mEncryptPassword = encryptPassword;
3871            }
3872            mCompress = doCompress;
3873        }
3874
3875        void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
3876            for (String pkgName : pkgNames) {
3877                if (!set.containsKey(pkgName)) {
3878                    try {
3879                        PackageInfo info = mPackageManager.getPackageInfo(pkgName,
3880                                PackageManager.GET_SIGNATURES);
3881                        set.put(pkgName, info);
3882                    } catch (NameNotFoundException e) {
3883                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
3884                    }
3885                }
3886            }
3887        }
3888
3889        private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
3890                OutputStream ofstream) throws Exception {
3891            // User key will be used to encrypt the master key.
3892            byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE);
3893            SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt,
3894                    PBKDF2_HASH_ROUNDS);
3895
3896            // the master key is random for each backup
3897            byte[] masterPw = new byte[256 / 8];
3898            mRng.nextBytes(masterPw);
3899            byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE);
3900
3901            // primary encryption of the datastream with the random key
3902            Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
3903            SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES");
3904            c.init(Cipher.ENCRYPT_MODE, masterKeySpec);
3905            OutputStream finalOutput = new CipherOutputStream(ofstream, c);
3906
3907            // line 4: name of encryption algorithm
3908            headerbuf.append(ENCRYPTION_ALGORITHM_NAME);
3909            headerbuf.append('\n');
3910            // line 5: user password salt [hex]
3911            headerbuf.append(byteArrayToHex(newUserSalt));
3912            headerbuf.append('\n');
3913            // line 6: master key checksum salt [hex]
3914            headerbuf.append(byteArrayToHex(checksumSalt));
3915            headerbuf.append('\n');
3916            // line 7: number of PBKDF2 rounds used [decimal]
3917            headerbuf.append(PBKDF2_HASH_ROUNDS);
3918            headerbuf.append('\n');
3919
3920            // line 8: IV of the user key [hex]
3921            Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding");
3922            mkC.init(Cipher.ENCRYPT_MODE, userKey);
3923
3924            byte[] IV = mkC.getIV();
3925            headerbuf.append(byteArrayToHex(IV));
3926            headerbuf.append('\n');
3927
3928            // line 9: master IV + key blob, encrypted by the user key [hex].  Blob format:
3929            //    [byte] IV length = Niv
3930            //    [array of Niv bytes] IV itself
3931            //    [byte] master key length = Nmk
3932            //    [array of Nmk bytes] master key itself
3933            //    [byte] MK checksum hash length = Nck
3934            //    [array of Nck bytes] master key checksum hash
3935            //
3936            // The checksum is the (master key + checksum salt), run through the
3937            // stated number of PBKDF2 rounds
3938            IV = c.getIV();
3939            byte[] mk = masterKeySpec.getEncoded();
3940            byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(),
3941                    checksumSalt, PBKDF2_HASH_ROUNDS);
3942
3943            ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length
3944                    + checksum.length + 3);
3945            DataOutputStream mkOut = new DataOutputStream(blob);
3946            mkOut.writeByte(IV.length);
3947            mkOut.write(IV);
3948            mkOut.writeByte(mk.length);
3949            mkOut.write(mk);
3950            mkOut.writeByte(checksum.length);
3951            mkOut.write(checksum);
3952            mkOut.flush();
3953            byte[] encryptedMk = mkC.doFinal(blob.toByteArray());
3954            headerbuf.append(byteArrayToHex(encryptedMk));
3955            headerbuf.append('\n');
3956
3957            return finalOutput;
3958        }
3959
3960        private void finalizeBackup(OutputStream out) {
3961            try {
3962                // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes.
3963                byte[] eof = new byte[512 * 2]; // newly allocated == zero filled
3964                out.write(eof);
3965            } catch (IOException e) {
3966                Slog.w(TAG, "Error attempting to finalize backup stream");
3967            }
3968        }
3969
3970        @Override
3971        public void run() {
3972            Slog.i(TAG, "--- Performing full-dataset adb backup ---");
3973
3974            TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
3975            FullBackupObbConnection obbConnection = new FullBackupObbConnection();
3976            obbConnection.establish();  // we'll want this later
3977
3978            sendStartBackup();
3979
3980            // doAllApps supersedes the package set if any
3981            if (mAllApps) {
3982                List<PackageInfo> allPackages = mPackageManager.getInstalledPackages(
3983                        PackageManager.GET_SIGNATURES);
3984                for (int i = 0; i < allPackages.size(); i++) {
3985                    PackageInfo pkg = allPackages.get(i);
3986                    // Exclude system apps if we've been asked to do so
3987                    if (mIncludeSystem == true
3988                            || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
3989                        packagesToBackup.put(pkg.packageName, pkg);
3990                    }
3991                }
3992            }
3993
3994            // If we're doing widget state as well, ensure that we have all the involved
3995            // host & provider packages in the set
3996            if (mDoWidgets) {
3997                // TODO: http://b/22388012
3998                List<String> pkgs =
3999                        AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_SYSTEM);
4000                if (pkgs != null) {
4001                    if (MORE_DEBUG) {
4002                        Slog.i(TAG, "Adding widget participants to backup set:");
4003                        StringBuilder sb = new StringBuilder(128);
4004                        sb.append("   ");
4005                        for (String s : pkgs) {
4006                            sb.append(' ');
4007                            sb.append(s);
4008                        }
4009                        Slog.i(TAG, sb.toString());
4010                    }
4011                    addPackagesToSet(packagesToBackup, pkgs);
4012                }
4013            }
4014
4015            // Now process the command line argument packages, if any. Note that explicitly-
4016            // named system-partition packages will be included even if includeSystem was
4017            // set to false.
4018            if (mPackages != null) {
4019                addPackagesToSet(packagesToBackup, mPackages);
4020            }
4021
4022            // Now we cull any inapplicable / inappropriate packages from the set.  This
4023            // includes the special shared-storage agent package; we handle that one
4024            // explicitly at the end of the backup pass.
4025            Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
4026            while (iter.hasNext()) {
4027                PackageInfo pkg = iter.next().getValue();
4028                if (!appIsEligibleForBackup(pkg.applicationInfo)) {
4029                    iter.remove();
4030                }
4031            }
4032
4033            // flatten the set of packages now so we can explicitly control the ordering
4034            ArrayList<PackageInfo> backupQueue =
4035                    new ArrayList<PackageInfo>(packagesToBackup.values());
4036            FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
4037            OutputStream out = null;
4038
4039            PackageInfo pkg = null;
4040            try {
4041                boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
4042
4043                // Only allow encrypted backups of encrypted devices
4044                if (deviceIsEncrypted() && !encrypting) {
4045                    Slog.e(TAG, "Unencrypted backup of encrypted device; aborting");
4046                    return;
4047                }
4048
4049                OutputStream finalOutput = ofstream;
4050
4051                // Verify that the given password matches the currently-active
4052                // backup password, if any
4053                if (!backupPasswordMatches(mCurrentPassword)) {
4054                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
4055                    return;
4056                }
4057
4058                // Write the global file header.  All strings are UTF-8 encoded; lines end
4059                // with a '\n' byte.  Actual backup data begins immediately following the
4060                // final '\n'.
4061                //
4062                // line 1: "ANDROID BACKUP"
4063                // line 2: backup file format version, currently "2"
4064                // line 3: compressed?  "0" if not compressed, "1" if compressed.
4065                // line 4: name of encryption algorithm [currently only "none" or "AES-256"]
4066                //
4067                // When line 4 is not "none", then additional header data follows:
4068                //
4069                // line 5: user password salt [hex]
4070                // line 6: master key checksum salt [hex]
4071                // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal]
4072                // line 8: IV of the user key [hex]
4073                // line 9: master key blob [hex]
4074                //     IV of the master key, master key itself, master key checksum hash
4075                //
4076                // The master key checksum is the master key plus its checksum salt, run through
4077                // 10k rounds of PBKDF2.  This is used to verify that the user has supplied the
4078                // correct password for decrypting the archive:  the master key decrypted from
4079                // the archive using the user-supplied password is also run through PBKDF2 in
4080                // this way, and if the result does not match the checksum as stored in the
4081                // archive, then we know that the user-supplied password does not match the
4082                // archive's.
4083                StringBuilder headerbuf = new StringBuilder(1024);
4084
4085                headerbuf.append(BACKUP_FILE_HEADER_MAGIC);
4086                headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n
4087                headerbuf.append(mCompress ? "\n1\n" : "\n0\n");
4088
4089                try {
4090                    // Set up the encryption stage if appropriate, and emit the correct header
4091                    if (encrypting) {
4092                        finalOutput = emitAesBackupHeader(headerbuf, finalOutput);
4093                    } else {
4094                        headerbuf.append("none\n");
4095                    }
4096
4097                    byte[] header = headerbuf.toString().getBytes("UTF-8");
4098                    ofstream.write(header);
4099
4100                    // Set up the compression stage feeding into the encryption stage (if any)
4101                    if (mCompress) {
4102                        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
4103                        finalOutput = new DeflaterOutputStream(finalOutput, deflater, true);
4104                    }
4105
4106                    out = finalOutput;
4107                } catch (Exception e) {
4108                    // Should never happen!
4109                    Slog.e(TAG, "Unable to emit archive header", e);
4110                    return;
4111                }
4112
4113                // Shared storage if requested
4114                if (mIncludeShared) {
4115                    try {
4116                        pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
4117                        backupQueue.add(pkg);
4118                    } catch (NameNotFoundException e) {
4119                        Slog.e(TAG, "Unable to find shared-storage backup handler");
4120                    }
4121                }
4122
4123                // Now actually run the constructed backup sequence
4124                int N = backupQueue.size();
4125                for (int i = 0; i < N; i++) {
4126                    pkg = backupQueue.get(i);
4127                    final boolean isSharedStorage =
4128                            pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
4129
4130                    mBackupEngine = new FullBackupEngine(out, pkg.packageName, null, mIncludeApks);
4131                    sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
4132                    mBackupEngine.backupOnePackage(pkg);
4133
4134                    // after the app's agent runs to handle its private filesystem
4135                    // contents, back up any OBB content it has on its behalf.
4136                    if (mIncludeObbs) {
4137                        boolean obbOkay = obbConnection.backupObbs(pkg, out);
4138                        if (!obbOkay) {
4139                            throw new RuntimeException("Failure writing OBB stack for " + pkg);
4140                        }
4141                    }
4142                }
4143
4144                // Done!
4145                finalizeBackup(out);
4146            } catch (RemoteException e) {
4147                Slog.e(TAG, "App died during full backup");
4148            } catch (Exception e) {
4149                Slog.e(TAG, "Internal exception during full backup", e);
4150            } finally {
4151                try {
4152                    if (out != null) out.close();
4153                    mOutputFile.close();
4154                } catch (IOException e) {
4155                    /* nothing we can do about this */
4156                }
4157                synchronized (mCurrentOpLock) {
4158                    mCurrentOperations.clear();
4159                }
4160                synchronized (mLatch) {
4161                    mLatch.set(true);
4162                    mLatch.notifyAll();
4163                }
4164                sendEndBackup();
4165                obbConnection.tearDown();
4166                if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
4167                mWakelock.release();
4168            }
4169        }
4170    }
4171
4172    // Full backup task extension used for transport-oriented operation
4173    class PerformFullTransportBackupTask extends FullBackupTask {
4174        static final String TAG = "PFTBT";
4175        ArrayList<PackageInfo> mPackages;
4176        boolean mUpdateSchedule;
4177        CountDownLatch mLatch;
4178        AtomicBoolean mKeepRunning;     // signal from job scheduler
4179        FullBackupJob mJob;             // if a scheduled job needs to be finished afterwards
4180        IBackupObserver mBackupObserver;
4181        boolean mUserInitiated;
4182
4183        PerformFullTransportBackupTask(IFullBackupRestoreObserver observer,
4184                String[] whichPackages, boolean updateSchedule,
4185                FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver,
4186                boolean userInitiated) {
4187            super(observer);
4188            mUpdateSchedule = updateSchedule;
4189            mLatch = latch;
4190            mKeepRunning = new AtomicBoolean(true);
4191            mJob = runningJob;
4192            mPackages = new ArrayList<PackageInfo>(whichPackages.length);
4193            mBackupObserver = backupObserver;
4194            mUserInitiated = userInitiated;
4195
4196            for (String pkg : whichPackages) {
4197                try {
4198                    PackageInfo info = mPackageManager.getPackageInfo(pkg,
4199                            PackageManager.GET_SIGNATURES);
4200                    if (!appIsEligibleForBackup(info.applicationInfo)) {
4201                        // Cull any packages that have indicated that backups are not permitted,
4202                        // that run as system-domain uids but do not define their own backup agents,
4203                        // as well as any explicit mention of the 'special' shared-storage agent
4204                        // package (we handle that one at the end).
4205                        if (MORE_DEBUG) {
4206                            Slog.d(TAG, "Ignoring ineligible package " + pkg);
4207                        }
4208                        sendBackupOnResult(mBackupObserver, pkg,
4209                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4210                        continue;
4211                    } else if (!appGetsFullBackup(info)) {
4212                        // Cull any packages that are found in the queue but now aren't supposed
4213                        // to get full-data backup operations.
4214                        if (MORE_DEBUG) {
4215                            Slog.d(TAG, "Ignoring full-data backup of key/value participant "
4216                                    + pkg);
4217                        }
4218                        sendBackupOnResult(mBackupObserver, pkg,
4219                                BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4220                        continue;
4221                    } else if (appIsStopped(info.applicationInfo)) {
4222                        // Cull any packages in the 'stopped' state: they've either just been
4223                        // installed or have explicitly been force-stopped by the user.  In both
4224                        // cases we do not want to launch them for backup.
4225                        if (MORE_DEBUG) {
4226                            Slog.d(TAG, "Ignoring stopped package " + pkg);
4227                        }
4228                        sendBackupOnResult(mBackupObserver, pkg,
4229                                BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4230                        continue;
4231                    }
4232                    mPackages.add(info);
4233                } catch (NameNotFoundException e) {
4234                    Slog.i(TAG, "Requested package " + pkg + " not found; ignoring");
4235                }
4236            }
4237        }
4238
4239        public void setRunning(boolean running) {
4240            mKeepRunning.set(running);
4241        }
4242
4243        @Override
4244        public void run() {
4245            // data from the app, passed to us for bridging to the transport
4246            ParcelFileDescriptor[] enginePipes = null;
4247
4248            // Pipe through which we write data to the transport
4249            ParcelFileDescriptor[] transportPipes = null;
4250
4251            PackageInfo currentPackage;
4252            long backoff = 0;
4253
4254            try {
4255                if (!mEnabled || !mProvisioned) {
4256                    // Backups are globally disabled, so don't proceed.
4257                    if (DEBUG) {
4258                        Slog.i(TAG, "full backup requested but e=" + mEnabled
4259                                + " p=" + mProvisioned + "; ignoring");
4260                    }
4261                    mUpdateSchedule = false;
4262                    sendBackupFinished(mBackupObserver, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
4263                    return;
4264                }
4265
4266                IBackupTransport transport = getTransport(mCurrentTransport);
4267                if (transport == null) {
4268                    Slog.w(TAG, "Transport not present; full data backup not performed");
4269                    sendBackupFinished(mBackupObserver, BackupManager.ERROR_TRANSPORT_ABORTED);
4270                    return;
4271                }
4272
4273                // Set up to send data to the transport
4274                final int N = mPackages.size();
4275                final byte[] buffer = new byte[8192];
4276                for (int i = 0; i < N; i++) {
4277                    currentPackage = mPackages.get(i);
4278                    if (DEBUG) {
4279                        Slog.i(TAG, "Initiating full-data transport backup of "
4280                                + currentPackage.packageName);
4281                    }
4282                    EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE,
4283                            currentPackage.packageName);
4284
4285                    transportPipes = ParcelFileDescriptor.createPipe();
4286
4287                    // Tell the transport the data's coming
4288                    int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
4289                    int result = transport.performFullBackup(currentPackage,
4290                            transportPipes[0], flags);
4291                    if (result == BackupTransport.TRANSPORT_OK) {
4292                        // The transport has its own copy of the read end of the pipe,
4293                        // so close ours now
4294                        transportPipes[0].close();
4295                        transportPipes[0] = null;
4296
4297                        // Now set up the backup engine / data source end of things
4298                        enginePipes = ParcelFileDescriptor.createPipe();
4299                        CountDownLatch runnerLatch = new CountDownLatch(1);
4300                        SinglePackageBackupRunner backupRunner =
4301                                new SinglePackageBackupRunner(enginePipes[1], currentPackage,
4302                                        transport, runnerLatch);
4303                        // The runner dup'd the pipe half, so we close it here
4304                        enginePipes[1].close();
4305                        enginePipes[1] = null;
4306
4307                        // Spin off the runner to fetch the app's data and pipe it
4308                        // into the engine pipes
4309                        (new Thread(backupRunner, "package-backup-bridge")).start();
4310
4311                        // Read data off the engine pipe and pass it to the transport
4312                        // pipe until we hit EOD on the input stream.
4313                        FileInputStream in = new FileInputStream(
4314                                enginePipes[0].getFileDescriptor());
4315                        FileOutputStream out = new FileOutputStream(
4316                                transportPipes[1].getFileDescriptor());
4317                        long totalRead = 0;
4318                        final long expectedSize = backupRunner.expectedSize();
4319                        if (expectedSize < 0) {
4320                            result = BackupTransport.AGENT_ERROR;
4321                            sendBackupOnResult(mBackupObserver, currentPackage.packageName,
4322                                    BackupManager.ERROR_AGENT_FAILURE);
4323                        }
4324                        int nRead = 0;
4325                        do {
4326                            if (!mKeepRunning.get()) {
4327                                if (DEBUG_SCHEDULING) {
4328                                    Slog.i(TAG, "Full backup task told to stop");
4329                                }
4330                                break;
4331                            }
4332                            nRead = in.read(buffer);
4333                            if (MORE_DEBUG) {
4334                                Slog.v(TAG, "in.read(buffer) from app: " + nRead);
4335                            }
4336                            if (nRead > 0) {
4337                                out.write(buffer, 0, nRead);
4338                                result = transport.sendBackupData(nRead);
4339                                totalRead += nRead;
4340                                if (mBackupObserver != null && expectedSize > 0) {
4341                                    sendBackupOnUpdate(mBackupObserver, currentPackage.packageName,
4342                                        new BackupProgress(expectedSize, totalRead));
4343                                }
4344                            }
4345                        } while (nRead > 0 && result == BackupTransport.TRANSPORT_OK);
4346
4347                        // If we've lost our running criteria, tell the transport to cancel
4348                        // and roll back this (partial) backup payload; otherwise tell it
4349                        // that we've reached the clean finish state.
4350                        if (!mKeepRunning.get()) {
4351                            result = BackupTransport.TRANSPORT_ERROR;
4352                            transport.cancelFullBackup();
4353                        } else {
4354                            // If we were otherwise in a good state, now interpret the final
4355                            // result based on what finishBackup() returns.  If we're in a
4356                            // failure case already, preserve that result and ignore whatever
4357                            // finishBackup() reports.
4358                            final int finishResult = transport.finishBackup();
4359                            if (result == BackupTransport.TRANSPORT_OK) {
4360                                result = finishResult;
4361                            }
4362                        }
4363
4364                        if (MORE_DEBUG) {
4365                            Slog.i(TAG, "Done trying to send backup data: result=" + result);
4366                        }
4367
4368                        if (result != BackupTransport.TRANSPORT_OK) {
4369                            Slog.e(TAG, "Error " + result
4370                                    + " backing up " + currentPackage.packageName);
4371                        }
4372
4373                        // Also ask the transport how long it wants us to wait before
4374                        // moving on to the next package, if any.
4375                        backoff = transport.requestFullBackupTime();
4376                        if (DEBUG_SCHEDULING) {
4377                            Slog.i(TAG, "Transport suggested backoff=" + backoff);
4378                        }
4379
4380                    }
4381
4382                    // Roll this package to the end of the backup queue if we're
4383                    // in a queue-driven mode (regardless of success/failure)
4384                    if (mUpdateSchedule) {
4385                        enqueueFullBackup(currentPackage.packageName,
4386                                System.currentTimeMillis());
4387                    }
4388
4389                    if (result == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
4390                        if (DEBUG) {
4391                            Slog.i(TAG, "Transport rejected backup of "
4392                                    + currentPackage.packageName
4393                                    + ", skipping");
4394                        }
4395                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE,
4396                                currentPackage.packageName, "transport rejected");
4397                        sendBackupOnResult(mBackupObserver, currentPackage.packageName,
4398                            BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
4399                        // do nothing, clean up, and continue looping
4400                    } else if (result != BackupTransport.TRANSPORT_OK) {
4401                        Slog.w(TAG, "Transport failed; aborting backup: " + result);
4402                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
4403                        sendBackupOnResult(mBackupObserver, currentPackage.packageName,
4404                            BackupManager.ERROR_TRANSPORT_ABORTED);
4405                        return;
4406                    } else {
4407                        // Success!
4408                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS,
4409                                currentPackage.packageName);
4410                        logBackupComplete(currentPackage.packageName);
4411                        sendBackupOnResult(mBackupObserver, currentPackage.packageName,
4412                            BackupManager.SUCCESS);
4413                    }
4414                    cleanUpPipes(transportPipes);
4415                    cleanUpPipes(enginePipes);
4416                    currentPackage = null;
4417                }
4418
4419                if (DEBUG) {
4420                    Slog.i(TAG, "Full backup completed.");
4421                }
4422                sendBackupFinished(mBackupObserver, BackupManager.SUCCESS);
4423            } catch (Exception e) {
4424                Slog.w(TAG, "Exception trying full transport backup", e);
4425                sendBackupFinished(mBackupObserver, BackupManager.ERROR_TRANSPORT_ABORTED);
4426            } finally {
4427                cleanUpPipes(transportPipes);
4428                cleanUpPipes(enginePipes);
4429
4430                if (mJob != null) {
4431                    mJob.finishBackupPass();
4432                }
4433
4434                synchronized (mQueueLock) {
4435                    mRunningFullBackupTask = null;
4436                }
4437
4438
4439                mLatch.countDown();
4440
4441                // Now that we're actually done with schedule-driven work, reschedule
4442                // the next pass based on the new queue state.
4443                if (mUpdateSchedule) {
4444                    scheduleNextFullBackupJob(backoff);
4445                }
4446                Slog.i(BackupManagerService.TAG, "Full data backup pass finished.");
4447                mWakelock.release();
4448            }
4449        }
4450
4451        void cleanUpPipes(ParcelFileDescriptor[] pipes) {
4452            if (pipes != null) {
4453                if (pipes[0] != null) {
4454                    ParcelFileDescriptor fd = pipes[0];
4455                    pipes[0] = null;
4456                    try {
4457                        fd.close();
4458                    } catch (IOException e) {
4459                        Slog.w(TAG, "Unable to close pipe!");
4460                    }
4461                }
4462                if (pipes[1] != null) {
4463                    ParcelFileDescriptor fd = pipes[1];
4464                    pipes[1] = null;
4465                    try {
4466                        fd.close();
4467                    } catch (IOException e) {
4468                        Slog.w(TAG, "Unable to close pipe!");
4469                    }
4470                }
4471            }
4472        }
4473
4474        // Run the backup and pipe it back to the given socket -- expects to run on
4475        // a standalone thread.  The  runner owns this half of the pipe, and closes
4476        // it to indicate EOD to the other end.
4477        class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
4478            final AtomicInteger mResult = new AtomicInteger();
4479            final CountDownLatch mLatch = new CountDownLatch(1);
4480            final IBackupTransport mTransport;
4481
4482            public SinglePackageBackupPreflight(IBackupTransport transport) {
4483                mTransport = transport;
4484            }
4485
4486            @Override
4487            public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
4488                int result;
4489                try {
4490                    final int token = generateToken();
4491                    prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this);
4492                    addBackupTrace("preflighting");
4493                    if (MORE_DEBUG) {
4494                        Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
4495                    }
4496                    agent.doMeasureFullBackup(token, mBackupManagerBinder);
4497
4498                    // now wait to get our result back
4499                    mLatch.await();
4500                    int totalSize = mResult.get();
4501                    if (MORE_DEBUG) {
4502                        Slog.v(TAG, "Got preflight response; size=" + totalSize);
4503                    }
4504
4505                    result = mTransport.checkFullBackupSize(totalSize);
4506                } catch (Exception e) {
4507                    Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
4508                    result = BackupTransport.AGENT_ERROR;
4509                }
4510                return result;
4511            }
4512
4513            @Override
4514            public void execute() {
4515                // Unused in this case
4516            }
4517
4518            @Override
4519            public void operationComplete(int result) {
4520                // got the callback, and our preflightFullBackup() method is waiting for the result
4521                if (MORE_DEBUG) {
4522                    Slog.i(TAG, "Preflight op complete, result=" + result);
4523                }
4524                mResult.set(result);
4525                mLatch.countDown();
4526            }
4527
4528            @Override
4529            public void handleTimeout() {
4530                if (MORE_DEBUG) {
4531                    Slog.i(TAG, "Preflight timeout; failing");
4532                }
4533                mResult.set(BackupTransport.AGENT_ERROR);
4534                mLatch.countDown();
4535            }
4536
4537            @Override
4538            public long expectedSize() {
4539                try {
4540                    mLatch.await();
4541                    return mResult.get();
4542                } catch (InterruptedException e) {
4543                    return BackupTransport.NO_MORE_DATA;
4544                }
4545            }
4546        }
4547
4548        class SinglePackageBackupRunner implements Runnable {
4549            final ParcelFileDescriptor mOutput;
4550            final PackageInfo mTarget;
4551            final FullBackupPreflight mPreflight;
4552            final CountDownLatch mLatch;
4553
4554            SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
4555                    IBackupTransport transport, CountDownLatch latch) throws IOException {
4556                mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
4557                mTarget = target;
4558                mPreflight = new SinglePackageBackupPreflight(transport);
4559                mLatch = latch;
4560            }
4561
4562            @Override
4563            public void run() {
4564                try {
4565                    FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
4566                    FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName,
4567                            mPreflight, false);
4568                    engine.backupOnePackage(mTarget);
4569                } catch (Exception e) {
4570                    Slog.e(TAG, "Exception during full package backup of " + mTarget);
4571                } finally {
4572                    mLatch.countDown();
4573                    try {
4574                        mOutput.close();
4575                    } catch (IOException e) {
4576                        Slog.w(TAG, "Error closing transport pipe in runner");
4577                    }
4578                }
4579            }
4580
4581            long expectedSize() {
4582                return mPreflight.expectedSize();
4583            }
4584        }
4585    }
4586
4587    // ----- Full-data backup scheduling -----
4588
4589    /**
4590     * Schedule a job to tell us when it's a good time to run a full backup
4591     */
4592    void scheduleNextFullBackupJob(long transportMinLatency) {
4593        synchronized (mQueueLock) {
4594            if (mFullBackupQueue.size() > 0) {
4595                // schedule the next job at the point in the future when the least-recently
4596                // backed up app comes due for backup again; or immediately if it's already
4597                // due.
4598                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
4599                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
4600                final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
4601                        ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
4602                final long latency = Math.max(transportMinLatency, appLatency);
4603                Runnable r = new Runnable() {
4604                    @Override public void run() {
4605                        FullBackupJob.schedule(mContext, latency);
4606                    }
4607                };
4608                mBackupHandler.postDelayed(r, 2500);
4609            } else {
4610                if (DEBUG_SCHEDULING) {
4611                    Slog.i(TAG, "Full backup queue empty; not scheduling");
4612                }
4613            }
4614        }
4615    }
4616
4617    /**
4618     * Remove a package from the full-data queue.
4619     */
4620    void dequeueFullBackupLocked(String packageName) {
4621        final int N = mFullBackupQueue.size();
4622        for (int i = N-1; i >= 0; i--) {
4623            final FullBackupEntry e = mFullBackupQueue.get(i);
4624            if (packageName.equals(e.packageName)) {
4625                mFullBackupQueue.remove(i);
4626            }
4627        }
4628    }
4629
4630    /**
4631     * Enqueue full backup for the given app, with a note about when it last ran.
4632     */
4633    void enqueueFullBackup(String packageName, long lastBackedUp) {
4634        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
4635        synchronized (mQueueLock) {
4636            // First, sanity check that we aren't adding a duplicate.  Slow but
4637            // straightforward; we'll have at most on the order of a few hundred
4638            // items in this list.
4639            dequeueFullBackupLocked(packageName);
4640
4641            // This is also slow but easy for modest numbers of apps: work backwards
4642            // from the end of the queue until we find an item whose last backup
4643            // time was before this one, then insert this new entry after it.  If we're
4644            // adding something new we don't bother scanning, and just prepend.
4645            int which = -1;
4646            if (lastBackedUp > 0) {
4647                for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
4648                    final FullBackupEntry entry = mFullBackupQueue.get(which);
4649                    if (entry.lastBackup <= lastBackedUp) {
4650                        mFullBackupQueue.add(which + 1, newEntry);
4651                        break;
4652                    }
4653                }
4654            }
4655            if (which < 0) {
4656                // this one is earlier than any existing one, so prepend
4657                mFullBackupQueue.add(0, newEntry);
4658            }
4659        }
4660        writeFullBackupScheduleAsync();
4661    }
4662
4663    private boolean fullBackupAllowable(IBackupTransport transport) {
4664        if (transport == null) {
4665            Slog.w(TAG, "Transport not present; full data backup not performed");
4666            return false;
4667        }
4668
4669        // Don't proceed unless we have already established package metadata
4670        // for the current dataset via a key/value backup pass.
4671        try {
4672            File stateDir = new File(mBaseStateDir, transport.transportDirName());
4673            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
4674            if (pmState.length() <= 0) {
4675                if (DEBUG) {
4676                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
4677                }
4678                return false;
4679            }
4680        } catch (Exception e) {
4681            Slog.w(TAG, "Unable to contact transport");
4682            return false;
4683        }
4684
4685        return true;
4686    }
4687
4688    /**
4689     * Conditions are right for a full backup operation, so run one.  The model we use is
4690     * to perform one app backup per scheduled job execution, and to reschedule the job
4691     * with zero latency as long as conditions remain right and we still have work to do.
4692     *
4693     * <p>This is the "start a full backup operation" entry point called by the scheduled job.
4694     *
4695     * @return Whether ongoing work will continue.  The return value here will be passed
4696     *         along as the return value to the scheduled job's onStartJob() callback.
4697     */
4698    boolean beginFullBackup(FullBackupJob scheduledJob) {
4699        long now = System.currentTimeMillis();
4700        FullBackupEntry entry = null;
4701        long latency = MIN_FULL_BACKUP_INTERVAL;
4702
4703        if (!mEnabled || !mProvisioned) {
4704            // Backups are globally disabled, so don't proceed.  We also don't reschedule
4705            // the job driving automatic backups; that job will be scheduled again when
4706            // the user enables backup.
4707            if (MORE_DEBUG) {
4708                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
4709                        + " p=" + mProvisioned + "; ignoring");
4710            }
4711            return false;
4712        }
4713
4714        // Don't run the backup if we're in battery saver mode, but reschedule
4715        // to try again in the not-so-distant future.
4716        if (mPowerManager.isPowerSaveMode()) {
4717            if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
4718            FullBackupJob.schedule(mContext, KeyValueBackupJob.BATCH_INTERVAL);
4719            return false;
4720        }
4721
4722        if (DEBUG_SCHEDULING) {
4723            Slog.i(TAG, "Beginning scheduled full backup operation");
4724        }
4725
4726        // Great; we're able to run full backup jobs now.  See if we have any work to do.
4727        synchronized (mQueueLock) {
4728            if (mRunningFullBackupTask != null) {
4729                Slog.e(TAG, "Backup triggered but one already/still running!");
4730                return false;
4731            }
4732
4733            // At this point we think that we have work to do, but possibly not right now.
4734            // Any exit without actually running backups will also require that we
4735            // reschedule the job.
4736            boolean runBackup = true;
4737            boolean headBusy;
4738
4739            do {
4740                // Recheck each time, because culling due to ineligibility may
4741                // have emptied the queue.
4742                if (mFullBackupQueue.size() == 0) {
4743                    // no work to do so just bow out
4744                    if (DEBUG) {
4745                        Slog.i(TAG, "Backup queue empty; doing nothing");
4746                    }
4747                    runBackup = false;
4748                    break;
4749                }
4750
4751                headBusy = false;
4752
4753                if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
4754                    if (MORE_DEBUG) {
4755                        Slog.i(TAG, "Preconditions not met; not running full backup");
4756                    }
4757                    runBackup = false;
4758                    // Typically this means we haven't run a key/value backup yet.  Back off
4759                    // full-backup operations by the key/value job's run interval so that
4760                    // next time we run, we are likely to be able to make progress.
4761                    latency = KeyValueBackupJob.BATCH_INTERVAL;
4762                }
4763
4764                if (runBackup) {
4765                    entry = mFullBackupQueue.get(0);
4766                    long timeSinceRun = now - entry.lastBackup;
4767                    runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
4768                    if (!runBackup) {
4769                        // It's too early to back up the next thing in the queue, so bow out
4770                        if (MORE_DEBUG) {
4771                            Slog.i(TAG, "Device ready but too early to back up next app");
4772                        }
4773                        // Wait until the next app in the queue falls due for a full data backup
4774                        latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
4775                        break;  // we know we aren't doing work yet, so bail.
4776                    }
4777
4778                    try {
4779                        PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
4780                        if (!appGetsFullBackup(appInfo)) {
4781                            // The head app isn't supposed to get full-data backups [any more];
4782                            // so we cull it and force a loop around to consider the new head
4783                            // app.
4784                            if (MORE_DEBUG) {
4785                                Slog.i(TAG, "Culling package " + entry.packageName
4786                                        + " in full-backup queue but not eligible");
4787                            }
4788                            mFullBackupQueue.remove(0);
4789                            headBusy = true; // force the while() condition
4790                            continue;
4791                        }
4792
4793                        headBusy = mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
4794
4795                        if (headBusy) {
4796                            final long nextEligible = System.currentTimeMillis()
4797                                    + BUSY_BACKOFF_MIN_MILLIS
4798                                    + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
4799                            if (DEBUG_SCHEDULING) {
4800                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4801                                Slog.i(TAG, "Full backup time but " + entry.packageName
4802                                        + " is busy; deferring to "
4803                                        + sdf.format(new Date(nextEligible)));
4804                            }
4805                            // This relocates the app's entry from the head of the queue to
4806                            // its order-appropriate position further down, so upon looping
4807                            // a new candidate will be considered at the head.
4808                            enqueueFullBackup(entry.packageName,
4809                                    nextEligible - MIN_FULL_BACKUP_INTERVAL);
4810                        }
4811                    } catch (NameNotFoundException nnf) {
4812                        // So, we think we want to back this up, but it turns out the package
4813                        // in question is no longer installed.  We want to drop it from the
4814                        // queue entirely and move on, but if there's nothing else in the queue
4815                        // we should bail entirely.  headBusy cannot have been set to true yet.
4816                        runBackup = (mFullBackupQueue.size() > 1);
4817                    } catch (RemoteException e) {
4818                        // Cannot happen; the Activity Manager is in the same process
4819                    }
4820                }
4821            } while (headBusy);
4822
4823            if (!runBackup) {
4824                if (DEBUG_SCHEDULING) {
4825                    Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
4826                }
4827                final long deferTime = latency;     // pin for the closure
4828                mBackupHandler.post(new Runnable() {
4829                    @Override public void run() {
4830                        FullBackupJob.schedule(mContext, deferTime);
4831                    }
4832                });
4833                return false;
4834            }
4835
4836            // Okay, the top thing is ready for backup now.  Do it.
4837            mFullBackupQueue.remove(0);
4838            CountDownLatch latch = new CountDownLatch(1);
4839            String[] pkg = new String[] {entry.packageName};
4840            mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
4841                    scheduledJob, latch, null, false /* userInitiated */);
4842            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
4843            mWakelock.acquire();
4844            (new Thread(mRunningFullBackupTask)).start();
4845        }
4846
4847        return true;
4848    }
4849
4850    // The job scheduler says our constraints don't hold any more,
4851    // so tear down any ongoing backup task right away.
4852    void endFullBackup() {
4853        synchronized (mQueueLock) {
4854            if (mRunningFullBackupTask != null) {
4855                if (DEBUG_SCHEDULING) {
4856                    Slog.i(TAG, "Telling running backup to stop");
4857                }
4858                mRunningFullBackupTask.setRunning(false);
4859            }
4860        }
4861    }
4862
4863    // ----- Restore infrastructure -----
4864
4865    abstract class RestoreEngine {
4866        static final String TAG = "RestoreEngine";
4867
4868        public static final int SUCCESS = 0;
4869        public static final int TARGET_FAILURE = -2;
4870        public static final int TRANSPORT_FAILURE = -3;
4871
4872        private AtomicBoolean mRunning = new AtomicBoolean(false);
4873        private AtomicInteger mResult = new AtomicInteger(SUCCESS);
4874
4875        public boolean isRunning() {
4876            return mRunning.get();
4877        }
4878
4879        public void setRunning(boolean stillRunning) {
4880            synchronized (mRunning) {
4881                mRunning.set(stillRunning);
4882                mRunning.notifyAll();
4883            }
4884        }
4885
4886        public int waitForResult() {
4887            synchronized (mRunning) {
4888                while (isRunning()) {
4889                    try {
4890                        mRunning.wait();
4891                    } catch (InterruptedException e) {}
4892                }
4893            }
4894            return getResult();
4895        }
4896
4897        public int getResult() {
4898            return mResult.get();
4899        }
4900
4901        public void setResult(int result) {
4902            mResult.set(result);
4903        }
4904
4905        // TODO: abstract restore state and APIs
4906    }
4907
4908    // ----- Full restore from a file/socket -----
4909
4910    // Description of a file in the restore datastream
4911    static class FileMetadata {
4912        String packageName;             // name of the owning app
4913        String installerPackageName;    // name of the market-type app that installed the owner
4914        int type;                       // e.g. BackupAgent.TYPE_DIRECTORY
4915        String domain;                  // e.g. FullBackup.DATABASE_TREE_TOKEN
4916        String path;                    // subpath within the semantic domain
4917        long mode;                      // e.g. 0666 (actually int)
4918        long mtime;                     // last mod time, UTC time_t (actually int)
4919        long size;                      // bytes of content
4920
4921        @Override
4922        public String toString() {
4923            StringBuilder sb = new StringBuilder(128);
4924            sb.append("FileMetadata{");
4925            sb.append(packageName); sb.append(',');
4926            sb.append(type); sb.append(',');
4927            sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
4928            sb.append(size);
4929            sb.append('}');
4930            return sb.toString();
4931        }
4932    }
4933
4934    enum RestorePolicy {
4935        IGNORE,
4936        ACCEPT,
4937        ACCEPT_IF_APK
4938    }
4939
4940    // Full restore engine, used by both adb restore and transport-based full restore
4941    class FullRestoreEngine extends RestoreEngine {
4942        // Dedicated observer, if any
4943        IFullBackupRestoreObserver mObserver;
4944
4945        // Where we're delivering the file data as we go
4946        IBackupAgent mAgent;
4947
4948        // Are we permitted to only deliver a specific package's metadata?
4949        PackageInfo mOnlyPackage;
4950
4951        boolean mAllowApks;
4952        boolean mAllowObbs;
4953
4954        // Which package are we currently handling data for?
4955        String mAgentPackage;
4956
4957        // Info for working with the target app process
4958        ApplicationInfo mTargetApp;
4959
4960        // Machinery for restoring OBBs
4961        FullBackupObbConnection mObbConnection = null;
4962
4963        // possible handling states for a given package in the restore dataset
4964        final HashMap<String, RestorePolicy> mPackagePolicies
4965                = new HashMap<String, RestorePolicy>();
4966
4967        // installer package names for each encountered app, derived from the manifests
4968        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
4969
4970        // Signatures for a given package found in its manifest file
4971        final HashMap<String, Signature[]> mManifestSignatures
4972                = new HashMap<String, Signature[]>();
4973
4974        // Packages we've already wiped data on when restoring their first file
4975        final HashSet<String> mClearedPackages = new HashSet<String>();
4976
4977        // How much data have we moved?
4978        long mBytes;
4979
4980        // Working buffer
4981        byte[] mBuffer;
4982
4983        // Pipes for moving data
4984        ParcelFileDescriptor[] mPipes = null;
4985
4986        // Widget blob to be restored out-of-band
4987        byte[] mWidgetData = null;
4988
4989        // Runner that can be placed in a separate thread to do in-process
4990        // invocations of the full restore API asynchronously
4991        class RestoreFileRunnable implements Runnable {
4992            IBackupAgent mAgent;
4993            FileMetadata mInfo;
4994            ParcelFileDescriptor mSocket;
4995            int mToken;
4996
4997            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
4998                    ParcelFileDescriptor socket, int token) throws IOException {
4999                mAgent = agent;
5000                mInfo = info;
5001                mToken = token;
5002
5003                // This class is used strictly for process-local binder invocations.  The
5004                // semantics of ParcelFileDescriptor differ in this case; in particular, we
5005                // do not automatically get a 'dup'ed descriptor that we can can continue
5006                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
5007                // before proceeding to do the restore.
5008                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
5009            }
5010
5011            @Override
5012            public void run() {
5013                try {
5014                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
5015                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
5016                            mToken, mBackupManagerBinder);
5017                } catch (RemoteException e) {
5018                    // never happens; this is used strictly for local binder calls
5019                }
5020            }
5021        }
5022
5023        public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage,
5024                boolean allowApks, boolean allowObbs) {
5025            mObserver = observer;
5026            mOnlyPackage = onlyPackage;
5027            mAllowApks = allowApks;
5028            mAllowObbs = allowObbs;
5029            mBuffer = new byte[32 * 1024];
5030            mBytes = 0;
5031        }
5032
5033        public IBackupAgent getAgent() {
5034            return mAgent;
5035        }
5036
5037        public boolean restoreOneFile(InputStream instream, boolean mustKillAgent) {
5038            if (!isRunning()) {
5039                Slog.w(TAG, "Restore engine used after halting");
5040                return false;
5041            }
5042
5043            FileMetadata info;
5044            try {
5045                if (MORE_DEBUG) {
5046                    Slog.v(TAG, "Reading tar header for restoring file");
5047                }
5048                info = readTarHeaders(instream);
5049                if (info != null) {
5050                    if (MORE_DEBUG) {
5051                        dumpFileMetadata(info);
5052                    }
5053
5054                    final String pkg = info.packageName;
5055                    if (!pkg.equals(mAgentPackage)) {
5056                        // In the single-package case, it's a semantic error to expect
5057                        // one app's data but see a different app's on the wire
5058                        if (mOnlyPackage != null) {
5059                            if (!pkg.equals(mOnlyPackage.packageName)) {
5060                                Slog.w(TAG, "Expected data for " + mOnlyPackage
5061                                        + " but saw " + pkg);
5062                                setResult(RestoreEngine.TRANSPORT_FAILURE);
5063                                setRunning(false);
5064                                return false;
5065                            }
5066                        }
5067
5068                        // okay, change in package; set up our various
5069                        // bookkeeping if we haven't seen it yet
5070                        if (!mPackagePolicies.containsKey(pkg)) {
5071                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5072                        }
5073
5074                        // Clean up the previous agent relationship if necessary,
5075                        // and let the observer know we're considering a new app.
5076                        if (mAgent != null) {
5077                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
5078                            // Now we're really done
5079                            tearDownPipes();
5080                            tearDownAgent(mTargetApp);
5081                            mTargetApp = null;
5082                            mAgentPackage = null;
5083                        }
5084                    }
5085
5086                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
5087                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
5088                        mPackageInstallers.put(pkg, info.installerPackageName);
5089                        // We've read only the manifest content itself at this point,
5090                        // so consume the footer before looping around to the next
5091                        // input file
5092                        skipTarPadding(info.size, instream);
5093                        sendOnRestorePackage(pkg);
5094                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
5095                        // Metadata blobs!
5096                        readMetadata(info, instream);
5097                        skipTarPadding(info.size, instream);
5098                    } else {
5099                        // Non-manifest, so it's actual file data.  Is this a package
5100                        // we're ignoring?
5101                        boolean okay = true;
5102                        RestorePolicy policy = mPackagePolicies.get(pkg);
5103                        switch (policy) {
5104                            case IGNORE:
5105                                okay = false;
5106                                break;
5107
5108                            case ACCEPT_IF_APK:
5109                                // If we're in accept-if-apk state, then the first file we
5110                                // see MUST be the apk.
5111                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5112                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
5113                                    // Try to install the app.
5114                                    String installerName = mPackageInstallers.get(pkg);
5115                                    okay = installApk(info, installerName, instream);
5116                                    // good to go; promote to ACCEPT
5117                                    mPackagePolicies.put(pkg, (okay)
5118                                            ? RestorePolicy.ACCEPT
5119                                                    : RestorePolicy.IGNORE);
5120                                    // At this point we've consumed this file entry
5121                                    // ourselves, so just strip the tar footer and
5122                                    // go on to the next file in the input stream
5123                                    skipTarPadding(info.size, instream);
5124                                    return true;
5125                                } else {
5126                                    // File data before (or without) the apk.  We can't
5127                                    // handle it coherently in this case so ignore it.
5128                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5129                                    okay = false;
5130                                }
5131                                break;
5132
5133                            case ACCEPT:
5134                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5135                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
5136                                    // we can take the data without the apk, so we
5137                                    // *want* to do so.  skip the apk by declaring this
5138                                    // one file not-okay without changing the restore
5139                                    // policy for the package.
5140                                    okay = false;
5141                                }
5142                                break;
5143
5144                            default:
5145                                // Something has gone dreadfully wrong when determining
5146                                // the restore policy from the manifest.  Ignore the
5147                                // rest of this package's data.
5148                                Slog.e(TAG, "Invalid policy from manifest");
5149                                okay = false;
5150                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5151                                break;
5152                        }
5153
5154                        // Is it a *file* we need to drop?
5155                        if (!isRestorableFile(info)) {
5156                            okay = false;
5157                        }
5158
5159                        // If the policy is satisfied, go ahead and set up to pipe the
5160                        // data to the agent.
5161                        if (DEBUG && okay && mAgent != null) {
5162                            Slog.i(TAG, "Reusing existing agent instance");
5163                        }
5164                        if (okay && mAgent == null) {
5165                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
5166
5167                            try {
5168                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
5169
5170                                // If we haven't sent any data to this app yet, we probably
5171                                // need to clear it first.  Check that.
5172                                if (!mClearedPackages.contains(pkg)) {
5173                                    // apps with their own backup agents are
5174                                    // responsible for coherently managing a full
5175                                    // restore.
5176                                    if (mTargetApp.backupAgentName == null) {
5177                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
5178                                        clearApplicationDataSynchronous(pkg);
5179                                    } else {
5180                                        if (MORE_DEBUG) Slog.d(TAG, "backup agent ("
5181                                                + mTargetApp.backupAgentName + ") => no clear");
5182                                    }
5183                                    mClearedPackages.add(pkg);
5184                                } else {
5185                                    if (MORE_DEBUG) {
5186                                        Slog.d(TAG, "We've initialized this app already; no clear required");
5187                                    }
5188                                }
5189
5190                                // All set; now set up the IPC and launch the agent
5191                                setUpPipes();
5192                                mAgent = bindToAgentSynchronous(mTargetApp,
5193                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
5194                                mAgentPackage = pkg;
5195                            } catch (IOException e) {
5196                                // fall through to error handling
5197                            } catch (NameNotFoundException e) {
5198                                // fall through to error handling
5199                            }
5200
5201                            if (mAgent == null) {
5202                                Slog.e(TAG, "Unable to create agent for " + pkg);
5203                                okay = false;
5204                                tearDownPipes();
5205                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5206                            }
5207                        }
5208
5209                        // Sanity check: make sure we never give data to the wrong app.  This
5210                        // should never happen but a little paranoia here won't go amiss.
5211                        if (okay && !pkg.equals(mAgentPackage)) {
5212                            Slog.e(TAG, "Restoring data for " + pkg
5213                                    + " but agent is for " + mAgentPackage);
5214                            okay = false;
5215                        }
5216
5217                        // At this point we have an agent ready to handle the full
5218                        // restore data as well as a pipe for sending data to
5219                        // that agent.  Tell the agent to start reading from the
5220                        // pipe.
5221                        if (okay) {
5222                            boolean agentSuccess = true;
5223                            long toCopy = info.size;
5224                            final int token = generateToken();
5225                            try {
5226                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
5227                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
5228                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
5229                                            + " : " + info.path);
5230                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
5231                                            info.size, info.type, info.path, info.mode,
5232                                            info.mtime, token, mBackupManagerBinder);
5233                                } else {
5234                                    if (MORE_DEBUG) Slog.d(TAG, "Invoking agent to restore file "
5235                                            + info.path);
5236                                    // fire up the app's agent listening on the socket.  If
5237                                    // the agent is running in the system process we can't
5238                                    // just invoke it asynchronously, so we provide a thread
5239                                    // for it here.
5240                                    if (mTargetApp.processName.equals("system")) {
5241                                        Slog.d(TAG, "system process agent - spinning a thread");
5242                                        RestoreFileRunnable runner = new RestoreFileRunnable(
5243                                                mAgent, info, mPipes[0], token);
5244                                        new Thread(runner, "restore-sys-runner").start();
5245                                    } else {
5246                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
5247                                                info.domain, info.path, info.mode, info.mtime,
5248                                                token, mBackupManagerBinder);
5249                                    }
5250                                }
5251                            } catch (IOException e) {
5252                                // couldn't dup the socket for a process-local restore
5253                                Slog.d(TAG, "Couldn't establish restore");
5254                                agentSuccess = false;
5255                                okay = false;
5256                            } catch (RemoteException e) {
5257                                // whoops, remote entity went away.  We'll eat the content
5258                                // ourselves, then, and not copy it over.
5259                                Slog.e(TAG, "Agent crashed during full restore");
5260                                agentSuccess = false;
5261                                okay = false;
5262                            }
5263
5264                            // Copy over the data if the agent is still good
5265                            if (okay) {
5266                                if (MORE_DEBUG) {
5267                                    Slog.v(TAG, "  copying to restore agent: "
5268                                            + toCopy + " bytes");
5269                                }
5270                                boolean pipeOkay = true;
5271                                FileOutputStream pipe = new FileOutputStream(
5272                                        mPipes[1].getFileDescriptor());
5273                                while (toCopy > 0) {
5274                                    int toRead = (toCopy > mBuffer.length)
5275                                            ? mBuffer.length : (int)toCopy;
5276                                    int nRead = instream.read(mBuffer, 0, toRead);
5277                                    if (nRead >= 0) mBytes += nRead;
5278                                    if (nRead <= 0) break;
5279                                    toCopy -= nRead;
5280
5281                                    // send it to the output pipe as long as things
5282                                    // are still good
5283                                    if (pipeOkay) {
5284                                        try {
5285                                            pipe.write(mBuffer, 0, nRead);
5286                                        } catch (IOException e) {
5287                                            Slog.e(TAG, "Failed to write to restore pipe", e);
5288                                            pipeOkay = false;
5289                                        }
5290                                    }
5291                                }
5292
5293                                // done sending that file!  Now we just need to consume
5294                                // the delta from info.size to the end of block.
5295                                skipTarPadding(info.size, instream);
5296
5297                                // and now that we've sent it all, wait for the remote
5298                                // side to acknowledge receipt
5299                                agentSuccess = waitUntilOperationComplete(token);
5300                            }
5301
5302                            // okay, if the remote end failed at any point, deal with
5303                            // it by ignoring the rest of the restore on it
5304                            if (!agentSuccess) {
5305                                Slog.w(TAG, "Agent failure; ending restore");
5306                                mBackupHandler.removeMessages(MSG_TIMEOUT);
5307                                tearDownPipes();
5308                                tearDownAgent(mTargetApp);
5309                                mAgent = null;
5310                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5311
5312                                // If this was a single-package restore, we halt immediately
5313                                // with an agent error under these circumstances
5314                                if (mOnlyPackage != null) {
5315                                    setResult(RestoreEngine.TARGET_FAILURE);
5316                                    setRunning(false);
5317                                    return false;
5318                                }
5319                            }
5320                        }
5321
5322                        // Problems setting up the agent communication, an explicitly
5323                        // dropped file, or an already-ignored package: skip to the
5324                        // next stream entry by reading and discarding this file.
5325                        if (!okay) {
5326                            if (MORE_DEBUG) Slog.d(TAG, "[discarding file content]");
5327                            long bytesToConsume = (info.size + 511) & ~511;
5328                            while (bytesToConsume > 0) {
5329                                int toRead = (bytesToConsume > mBuffer.length)
5330                                        ? mBuffer.length : (int)bytesToConsume;
5331                                long nRead = instream.read(mBuffer, 0, toRead);
5332                                if (nRead >= 0) mBytes += nRead;
5333                                if (nRead <= 0) break;
5334                                bytesToConsume -= nRead;
5335                            }
5336                        }
5337                    }
5338                }
5339            } catch (IOException e) {
5340                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
5341                setResult(RestoreEngine.TRANSPORT_FAILURE);
5342                info = null;
5343            }
5344
5345            // If we got here we're either running smoothly or we've finished
5346            if (info == null) {
5347                if (MORE_DEBUG) {
5348                    Slog.i(TAG, "No [more] data for this package; tearing down");
5349                }
5350                tearDownPipes();
5351                setRunning(false);
5352                if (mustKillAgent) {
5353                    tearDownAgent(mTargetApp);
5354                }
5355            }
5356            return (info != null);
5357        }
5358
5359        void setUpPipes() throws IOException {
5360            mPipes = ParcelFileDescriptor.createPipe();
5361        }
5362
5363        void tearDownPipes() {
5364            if (mPipes != null) {
5365                try {
5366                    mPipes[0].close();
5367                    mPipes[0] = null;
5368                    mPipes[1].close();
5369                    mPipes[1] = null;
5370                } catch (IOException e) {
5371                    Slog.w(TAG, "Couldn't close agent pipes", e);
5372                }
5373                mPipes = null;
5374            }
5375        }
5376
5377        void tearDownAgent(ApplicationInfo app) {
5378            if (mAgent != null) {
5379                tearDownAgentAndKill(app);
5380                mAgent = null;
5381            }
5382        }
5383
5384        class RestoreInstallObserver extends PackageInstallObserver {
5385            final AtomicBoolean mDone = new AtomicBoolean();
5386            String mPackageName;
5387            int mResult;
5388
5389            public void reset() {
5390                synchronized (mDone) {
5391                    mDone.set(false);
5392                }
5393            }
5394
5395            public void waitForCompletion() {
5396                synchronized (mDone) {
5397                    while (mDone.get() == false) {
5398                        try {
5399                            mDone.wait();
5400                        } catch (InterruptedException e) { }
5401                    }
5402                }
5403            }
5404
5405            int getResult() {
5406                return mResult;
5407            }
5408
5409            @Override
5410            public void onPackageInstalled(String packageName, int returnCode,
5411                    String msg, Bundle extras) {
5412                synchronized (mDone) {
5413                    mResult = returnCode;
5414                    mPackageName = packageName;
5415                    mDone.set(true);
5416                    mDone.notifyAll();
5417                }
5418            }
5419        }
5420
5421        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
5422            final AtomicBoolean mDone = new AtomicBoolean();
5423            int mResult;
5424
5425            public void reset() {
5426                synchronized (mDone) {
5427                    mDone.set(false);
5428                }
5429            }
5430
5431            public void waitForCompletion() {
5432                synchronized (mDone) {
5433                    while (mDone.get() == false) {
5434                        try {
5435                            mDone.wait();
5436                        } catch (InterruptedException e) { }
5437                    }
5438                }
5439            }
5440
5441            @Override
5442            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
5443                synchronized (mDone) {
5444                    mResult = returnCode;
5445                    mDone.set(true);
5446                    mDone.notifyAll();
5447                }
5448            }
5449        }
5450
5451        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
5452        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
5453
5454        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
5455            boolean okay = true;
5456
5457            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
5458
5459            // The file content is an .apk file.  Copy it out to a staging location and
5460            // attempt to install it.
5461            File apkFile = new File(mDataDir, info.packageName);
5462            try {
5463                FileOutputStream apkStream = new FileOutputStream(apkFile);
5464                byte[] buffer = new byte[32 * 1024];
5465                long size = info.size;
5466                while (size > 0) {
5467                    long toRead = (buffer.length < size) ? buffer.length : size;
5468                    int didRead = instream.read(buffer, 0, (int)toRead);
5469                    if (didRead >= 0) mBytes += didRead;
5470                    apkStream.write(buffer, 0, didRead);
5471                    size -= didRead;
5472                }
5473                apkStream.close();
5474
5475                // make sure the installer can read it
5476                apkFile.setReadable(true, false);
5477
5478                // Now install it
5479                Uri packageUri = Uri.fromFile(apkFile);
5480                mInstallObserver.reset();
5481                mPackageManager.installPackage(packageUri, mInstallObserver,
5482                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
5483                        installerPackage);
5484                mInstallObserver.waitForCompletion();
5485
5486                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
5487                    // The only time we continue to accept install of data even if the
5488                    // apk install failed is if we had already determined that we could
5489                    // accept the data regardless.
5490                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
5491                        okay = false;
5492                    }
5493                } else {
5494                    // Okay, the install succeeded.  Make sure it was the right app.
5495                    boolean uninstall = false;
5496                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
5497                        Slog.w(TAG, "Restore stream claimed to include apk for "
5498                                + info.packageName + " but apk was really "
5499                                + mInstallObserver.mPackageName);
5500                        // delete the package we just put in place; it might be fraudulent
5501                        okay = false;
5502                        uninstall = true;
5503                    } else {
5504                        try {
5505                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
5506                                    PackageManager.GET_SIGNATURES);
5507                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
5508                                Slog.w(TAG, "Restore stream contains apk of package "
5509                                        + info.packageName + " but it disallows backup/restore");
5510                                okay = false;
5511                            } else {
5512                                // So far so good -- do the signatures match the manifest?
5513                                Signature[] sigs = mManifestSignatures.get(info.packageName);
5514                                if (signaturesMatch(sigs, pkg)) {
5515                                    // If this is a system-uid app without a declared backup agent,
5516                                    // don't restore any of the file data.
5517                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
5518                                            && (pkg.applicationInfo.backupAgentName == null)) {
5519                                        Slog.w(TAG, "Installed app " + info.packageName
5520                                                + " has restricted uid and no agent");
5521                                        okay = false;
5522                                    }
5523                                } else {
5524                                    Slog.w(TAG, "Installed app " + info.packageName
5525                                            + " signatures do not match restore manifest");
5526                                    okay = false;
5527                                    uninstall = true;
5528                                }
5529                            }
5530                        } catch (NameNotFoundException e) {
5531                            Slog.w(TAG, "Install of package " + info.packageName
5532                                    + " succeeded but now not found");
5533                            okay = false;
5534                        }
5535                    }
5536
5537                    // If we're not okay at this point, we need to delete the package
5538                    // that we just installed.
5539                    if (uninstall) {
5540                        mDeleteObserver.reset();
5541                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
5542                                mDeleteObserver, 0);
5543                        mDeleteObserver.waitForCompletion();
5544                    }
5545                }
5546            } catch (IOException e) {
5547                Slog.e(TAG, "Unable to transcribe restored apk for install");
5548                okay = false;
5549            } finally {
5550                apkFile.delete();
5551            }
5552
5553            return okay;
5554        }
5555
5556        // Given an actual file content size, consume the post-content padding mandated
5557        // by the tar format.
5558        void skipTarPadding(long size, InputStream instream) throws IOException {
5559            long partial = (size + 512) % 512;
5560            if (partial > 0) {
5561                final int needed = 512 - (int)partial;
5562                if (MORE_DEBUG) {
5563                    Slog.i(TAG, "Skipping tar padding: " + needed + " bytes");
5564                }
5565                byte[] buffer = new byte[needed];
5566                if (readExactly(instream, buffer, 0, needed) == needed) {
5567                    mBytes += needed;
5568                } else throw new IOException("Unexpected EOF in padding");
5569            }
5570        }
5571
5572        // Read a widget metadata file, returning the restored blob
5573        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
5574            // Fail on suspiciously large widget dump files
5575            if (info.size > 64 * 1024) {
5576                throw new IOException("Metadata too big; corrupt? size=" + info.size);
5577            }
5578
5579            byte[] buffer = new byte[(int) info.size];
5580            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5581                mBytes += info.size;
5582            } else throw new IOException("Unexpected EOF in widget data");
5583
5584            String[] str = new String[1];
5585            int offset = extractLine(buffer, 0, str);
5586            int version = Integer.parseInt(str[0]);
5587            if (version == BACKUP_MANIFEST_VERSION) {
5588                offset = extractLine(buffer, offset, str);
5589                final String pkg = str[0];
5590                if (info.packageName.equals(pkg)) {
5591                    // Data checks out -- the rest of the buffer is a concatenation of
5592                    // binary blobs as described in the comment at writeAppWidgetData()
5593                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
5594                            offset, buffer.length - offset);
5595                    DataInputStream in = new DataInputStream(bin);
5596                    while (bin.available() > 0) {
5597                        int token = in.readInt();
5598                        int size = in.readInt();
5599                        if (size > 64 * 1024) {
5600                            throw new IOException("Datum "
5601                                    + Integer.toHexString(token)
5602                                    + " too big; corrupt? size=" + info.size);
5603                        }
5604                        switch (token) {
5605                            case BACKUP_WIDGET_METADATA_TOKEN:
5606                            {
5607                                if (MORE_DEBUG) {
5608                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
5609                                }
5610                                mWidgetData = new byte[size];
5611                                in.read(mWidgetData);
5612                                break;
5613                            }
5614                            default:
5615                            {
5616                                if (DEBUG) {
5617                                    Slog.i(TAG, "Ignoring metadata blob "
5618                                            + Integer.toHexString(token)
5619                                            + " for " + info.packageName);
5620                                }
5621                                in.skipBytes(size);
5622                                break;
5623                            }
5624                        }
5625                    }
5626                } else {
5627                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
5628                            + " but widget data for " + pkg);
5629                }
5630            } else {
5631                Slog.w(TAG, "Unsupported metadata version " + version);
5632            }
5633        }
5634
5635        // Returns a policy constant
5636        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
5637                throws IOException {
5638            // Fail on suspiciously large manifest files
5639            if (info.size > 64 * 1024) {
5640                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
5641            }
5642
5643            byte[] buffer = new byte[(int) info.size];
5644            if (MORE_DEBUG) {
5645                Slog.i(TAG, "   readAppManifest() looking for " + info.size + " bytes, "
5646                        + mBytes + " already consumed");
5647            }
5648            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5649                mBytes += info.size;
5650            } else throw new IOException("Unexpected EOF in manifest");
5651
5652            RestorePolicy policy = RestorePolicy.IGNORE;
5653            String[] str = new String[1];
5654            int offset = 0;
5655
5656            try {
5657                offset = extractLine(buffer, offset, str);
5658                int version = Integer.parseInt(str[0]);
5659                if (version == BACKUP_MANIFEST_VERSION) {
5660                    offset = extractLine(buffer, offset, str);
5661                    String manifestPackage = str[0];
5662                    // TODO: handle <original-package>
5663                    if (manifestPackage.equals(info.packageName)) {
5664                        offset = extractLine(buffer, offset, str);
5665                        version = Integer.parseInt(str[0]);  // app version
5666                        offset = extractLine(buffer, offset, str);
5667                        // This is the platform version, which we don't use, but we parse it
5668                        // as a safety against corruption in the manifest.
5669                        Integer.parseInt(str[0]);
5670                        offset = extractLine(buffer, offset, str);
5671                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
5672                        offset = extractLine(buffer, offset, str);
5673                        boolean hasApk = str[0].equals("1");
5674                        offset = extractLine(buffer, offset, str);
5675                        int numSigs = Integer.parseInt(str[0]);
5676                        if (numSigs > 0) {
5677                            Signature[] sigs = new Signature[numSigs];
5678                            for (int i = 0; i < numSigs; i++) {
5679                                offset = extractLine(buffer, offset, str);
5680                                sigs[i] = new Signature(str[0]);
5681                            }
5682                            mManifestSignatures.put(info.packageName, sigs);
5683
5684                            // Okay, got the manifest info we need...
5685                            try {
5686                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
5687                                        info.packageName, PackageManager.GET_SIGNATURES);
5688                                // Fall through to IGNORE if the app explicitly disallows backup
5689                                final int flags = pkgInfo.applicationInfo.flags;
5690                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
5691                                    // Restore system-uid-space packages only if they have
5692                                    // defined a custom backup agent
5693                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
5694                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
5695                                        // Verify signatures against any installed version; if they
5696                                        // don't match, then we fall though and ignore the data.  The
5697                                        // signatureMatch() method explicitly ignores the signature
5698                                        // check for packages installed on the system partition, because
5699                                        // such packages are signed with the platform cert instead of
5700                                        // the app developer's cert, so they're different on every
5701                                        // device.
5702                                        if (signaturesMatch(sigs, pkgInfo)) {
5703                                            if (pkgInfo.versionCode >= version) {
5704                                                Slog.i(TAG, "Sig + version match; taking data");
5705                                                policy = RestorePolicy.ACCEPT;
5706                                            } else {
5707                                                // The data is from a newer version of the app than
5708                                                // is presently installed.  That means we can only
5709                                                // use it if the matching apk is also supplied.
5710                                                if (mAllowApks) {
5711                                                    Slog.i(TAG, "Data version " + version
5712                                                            + " is newer than installed version "
5713                                                            + pkgInfo.versionCode
5714                                                            + " - requiring apk");
5715                                                    policy = RestorePolicy.ACCEPT_IF_APK;
5716                                                } else {
5717                                                    Slog.i(TAG, "Data requires newer version "
5718                                                            + version + "; ignoring");
5719                                                    policy = RestorePolicy.IGNORE;
5720                                                }
5721                                            }
5722                                        } else {
5723                                            Slog.w(TAG, "Restore manifest signatures do not match "
5724                                                    + "installed application for " + info.packageName);
5725                                        }
5726                                    } else {
5727                                        Slog.w(TAG, "Package " + info.packageName
5728                                                + " is system level with no agent");
5729                                    }
5730                                } else {
5731                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
5732                                            + info.packageName + " but allowBackup=false");
5733                                }
5734                            } catch (NameNotFoundException e) {
5735                                // Okay, the target app isn't installed.  We can process
5736                                // the restore properly only if the dataset provides the
5737                                // apk file and we can successfully install it.
5738                                if (mAllowApks) {
5739                                    if (DEBUG) Slog.i(TAG, "Package " + info.packageName
5740                                            + " not installed; requiring apk in dataset");
5741                                    policy = RestorePolicy.ACCEPT_IF_APK;
5742                                } else {
5743                                    policy = RestorePolicy.IGNORE;
5744                                }
5745                            }
5746
5747                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
5748                                Slog.i(TAG, "Cannot restore package " + info.packageName
5749                                        + " without the matching .apk");
5750                            }
5751                        } else {
5752                            Slog.i(TAG, "Missing signature on backed-up package "
5753                                    + info.packageName);
5754                        }
5755                    } else {
5756                        Slog.i(TAG, "Expected package " + info.packageName
5757                                + " but restore manifest claims " + manifestPackage);
5758                    }
5759                } else {
5760                    Slog.i(TAG, "Unknown restore manifest version " + version
5761                            + " for package " + info.packageName);
5762                }
5763            } catch (NumberFormatException e) {
5764                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
5765            } catch (IllegalArgumentException e) {
5766                Slog.w(TAG, e.getMessage());
5767            }
5768
5769            return policy;
5770        }
5771
5772        // Builds a line from a byte buffer starting at 'offset', and returns
5773        // the index of the next unconsumed data in the buffer.
5774        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
5775            final int end = buffer.length;
5776            if (offset >= end) throw new IOException("Incomplete data");
5777
5778            int pos;
5779            for (pos = offset; pos < end; pos++) {
5780                byte c = buffer[pos];
5781                // at LF we declare end of line, and return the next char as the
5782                // starting point for the next time through
5783                if (c == '\n') {
5784                    break;
5785                }
5786            }
5787            outStr[0] = new String(buffer, offset, pos - offset);
5788            pos++;  // may be pointing an extra byte past the end but that's okay
5789            return pos;
5790        }
5791
5792        void dumpFileMetadata(FileMetadata info) {
5793            if (MORE_DEBUG) {
5794                StringBuilder b = new StringBuilder(128);
5795
5796                // mode string
5797                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
5798                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
5799                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
5800                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
5801                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
5802                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
5803                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
5804                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
5805                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
5806                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
5807                b.append(String.format(" %9d ", info.size));
5808
5809                Date stamp = new Date(info.mtime);
5810                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
5811
5812                b.append(info.packageName);
5813                b.append(" :: ");
5814                b.append(info.domain);
5815                b.append(" :: ");
5816                b.append(info.path);
5817
5818                Slog.i(TAG, b.toString());
5819            }
5820        }
5821
5822        // Consume a tar file header block [sequence] and accumulate the relevant metadata
5823        FileMetadata readTarHeaders(InputStream instream) throws IOException {
5824            byte[] block = new byte[512];
5825            FileMetadata info = null;
5826
5827            boolean gotHeader = readTarHeader(instream, block);
5828            if (gotHeader) {
5829                try {
5830                    // okay, presume we're okay, and extract the various metadata
5831                    info = new FileMetadata();
5832                    info.size = extractRadix(block, 124, 12, 8);
5833                    info.mtime = extractRadix(block, 136, 12, 8);
5834                    info.mode = extractRadix(block, 100, 8, 8);
5835
5836                    info.path = extractString(block, 345, 155); // prefix
5837                    String path = extractString(block, 0, 100);
5838                    if (path.length() > 0) {
5839                        if (info.path.length() > 0) info.path += '/';
5840                        info.path += path;
5841                    }
5842
5843                    // tar link indicator field: 1 byte at offset 156 in the header.
5844                    int typeChar = block[156];
5845                    if (typeChar == 'x') {
5846                        // pax extended header, so we need to read that
5847                        gotHeader = readPaxExtendedHeader(instream, info);
5848                        if (gotHeader) {
5849                            // and after a pax extended header comes another real header -- read
5850                            // that to find the real file type
5851                            gotHeader = readTarHeader(instream, block);
5852                        }
5853                        if (!gotHeader) throw new IOException("Bad or missing pax header");
5854
5855                        typeChar = block[156];
5856                    }
5857
5858                    switch (typeChar) {
5859                        case '0': info.type = BackupAgent.TYPE_FILE; break;
5860                        case '5': {
5861                            info.type = BackupAgent.TYPE_DIRECTORY;
5862                            if (info.size != 0) {
5863                                Slog.w(TAG, "Directory entry with nonzero size in header");
5864                                info.size = 0;
5865                            }
5866                            break;
5867                        }
5868                        case 0: {
5869                            // presume EOF
5870                            if (MORE_DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
5871                            return null;
5872                        }
5873                        default: {
5874                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
5875                            throw new IOException("Unknown entity type " + typeChar);
5876                        }
5877                    }
5878
5879                    // Parse out the path
5880                    //
5881                    // first: apps/shared/unrecognized
5882                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
5883                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
5884                        // File in shared storage.  !!! TODO: implement this.
5885                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
5886                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
5887                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
5888                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
5889                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
5890                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
5891                        // App content!  Parse out the package name and domain
5892
5893                        // strip the apps/ prefix
5894                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
5895
5896                        // extract the package name
5897                        int slash = info.path.indexOf('/');
5898                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
5899                        info.packageName = info.path.substring(0, slash);
5900                        info.path = info.path.substring(slash+1);
5901
5902                        // if it's a manifest or metadata payload we're done, otherwise parse
5903                        // out the domain into which the file will be restored
5904                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
5905                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
5906                            slash = info.path.indexOf('/');
5907                            if (slash < 0) {
5908                                throw new IOException("Illegal semantic path in non-manifest "
5909                                        + info.path);
5910                            }
5911                            info.domain = info.path.substring(0, slash);
5912                            info.path = info.path.substring(slash + 1);
5913                        }
5914                    }
5915                } catch (IOException e) {
5916                    if (DEBUG) {
5917                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
5918                        if (MORE_DEBUG) {
5919                            HEXLOG(block);
5920                        }
5921                    }
5922                    throw e;
5923                }
5924            }
5925            return info;
5926        }
5927
5928        private boolean isRestorableFile(FileMetadata info) {
5929            if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
5930                if (MORE_DEBUG) {
5931                    Slog.i(TAG, "Dropping cache file path " + info.path);
5932                }
5933                return false;
5934            }
5935
5936            if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
5937                // It's possible this is "no-backup" dir contents in an archive stream
5938                // produced on a device running a version of the OS that predates that
5939                // API.  Respect the no-backup intention and don't let the data get to
5940                // the app.
5941                if (info.path.startsWith("no_backup/")) {
5942                    if (MORE_DEBUG) {
5943                        Slog.i(TAG, "Dropping no_backup file path " + info.path);
5944                    }
5945                    return false;
5946                }
5947            }
5948
5949            // The path needs to be canonical
5950            if (info.path.contains("..") || info.path.contains("//")) {
5951                if (MORE_DEBUG) {
5952                    Slog.w(TAG, "Dropping invalid path " + info.path);
5953                }
5954                return false;
5955            }
5956
5957            // Otherwise we think this file is good to go
5958            return true;
5959        }
5960
5961        private void HEXLOG(byte[] block) {
5962            int offset = 0;
5963            int todo = block.length;
5964            StringBuilder buf = new StringBuilder(64);
5965            while (todo > 0) {
5966                buf.append(String.format("%04x   ", offset));
5967                int numThisLine = (todo > 16) ? 16 : todo;
5968                for (int i = 0; i < numThisLine; i++) {
5969                    buf.append(String.format("%02x ", block[offset+i]));
5970                }
5971                Slog.i("hexdump", buf.toString());
5972                buf.setLength(0);
5973                todo -= numThisLine;
5974                offset += numThisLine;
5975            }
5976        }
5977
5978        // Read exactly the given number of bytes into a buffer at the stated offset.
5979        // Returns false if EOF is encountered before the requested number of bytes
5980        // could be read.
5981        int readExactly(InputStream in, byte[] buffer, int offset, int size)
5982                throws IOException {
5983            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
5984if (MORE_DEBUG) Slog.i(TAG, "  ... readExactly(" + size + ") called");
5985            int soFar = 0;
5986            while (soFar < size) {
5987                int nRead = in.read(buffer, offset + soFar, size - soFar);
5988                if (nRead <= 0) {
5989                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
5990                    break;
5991                }
5992                soFar += nRead;
5993if (MORE_DEBUG) Slog.v(TAG, "   + got " + nRead + "; now wanting " + (size - soFar));
5994            }
5995            return soFar;
5996        }
5997
5998        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
5999            final int got = readExactly(instream, block, 0, 512);
6000            if (got == 0) return false;     // Clean EOF
6001            if (got < 512) throw new IOException("Unable to read full block header");
6002            mBytes += 512;
6003            return true;
6004        }
6005
6006        // overwrites 'info' fields based on the pax extended header
6007        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
6008                throws IOException {
6009            // We should never see a pax extended header larger than this
6010            if (info.size > 32*1024) {
6011                Slog.w(TAG, "Suspiciously large pax header size " + info.size
6012                        + " - aborting");
6013                throw new IOException("Sanity failure: pax header size " + info.size);
6014            }
6015
6016            // read whole blocks, not just the content size
6017            int numBlocks = (int)((info.size + 511) >> 9);
6018            byte[] data = new byte[numBlocks * 512];
6019            if (readExactly(instream, data, 0, data.length) < data.length) {
6020                throw new IOException("Unable to read full pax header");
6021            }
6022            mBytes += data.length;
6023
6024            final int contentSize = (int) info.size;
6025            int offset = 0;
6026            do {
6027                // extract the line at 'offset'
6028                int eol = offset+1;
6029                while (eol < contentSize && data[eol] != ' ') eol++;
6030                if (eol >= contentSize) {
6031                    // error: we just hit EOD looking for the end of the size field
6032                    throw new IOException("Invalid pax data");
6033                }
6034                // eol points to the space between the count and the key
6035                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
6036                int key = eol + 1;  // start of key=value
6037                eol = offset + linelen - 1; // trailing LF
6038                int value;
6039                for (value = key+1; data[value] != '=' && value <= eol; value++);
6040                if (value > eol) {
6041                    throw new IOException("Invalid pax declaration");
6042                }
6043
6044                // pax requires that key/value strings be in UTF-8
6045                String keyStr = new String(data, key, value-key, "UTF-8");
6046                // -1 to strip the trailing LF
6047                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
6048
6049                if ("path".equals(keyStr)) {
6050                    info.path = valStr;
6051                } else if ("size".equals(keyStr)) {
6052                    info.size = Long.parseLong(valStr);
6053                } else {
6054                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
6055                }
6056
6057                offset += linelen;
6058            } while (offset < contentSize);
6059
6060            return true;
6061        }
6062
6063        long extractRadix(byte[] data, int offset, int maxChars, int radix)
6064                throws IOException {
6065            long value = 0;
6066            final int end = offset + maxChars;
6067            for (int i = offset; i < end; i++) {
6068                final byte b = data[i];
6069                // Numeric fields in tar can terminate with either NUL or SPC
6070                if (b == 0 || b == ' ') break;
6071                if (b < '0' || b > ('0' + radix - 1)) {
6072                    throw new IOException("Invalid number in header: '" + (char)b
6073                            + "' for radix " + radix);
6074                }
6075                value = radix * value + (b - '0');
6076            }
6077            return value;
6078        }
6079
6080        String extractString(byte[] data, int offset, int maxChars) throws IOException {
6081            final int end = offset + maxChars;
6082            int eos = offset;
6083            // tar string fields terminate early with a NUL
6084            while (eos < end && data[eos] != 0) eos++;
6085            return new String(data, offset, eos-offset, "US-ASCII");
6086        }
6087
6088        void sendStartRestore() {
6089            if (mObserver != null) {
6090                try {
6091                    mObserver.onStartRestore();
6092                } catch (RemoteException e) {
6093                    Slog.w(TAG, "full restore observer went away: startRestore");
6094                    mObserver = null;
6095                }
6096            }
6097        }
6098
6099        void sendOnRestorePackage(String name) {
6100            if (mObserver != null) {
6101                try {
6102                    // TODO: use a more user-friendly name string
6103                    mObserver.onRestorePackage(name);
6104                } catch (RemoteException e) {
6105                    Slog.w(TAG, "full restore observer went away: restorePackage");
6106                    mObserver = null;
6107                }
6108            }
6109        }
6110
6111        void sendEndRestore() {
6112            if (mObserver != null) {
6113                try {
6114                    mObserver.onEndRestore();
6115                } catch (RemoteException e) {
6116                    Slog.w(TAG, "full restore observer went away: endRestore");
6117                    mObserver = null;
6118                }
6119            }
6120        }
6121    }
6122
6123    // ***** end new engine class ***
6124
6125    class PerformAdbRestoreTask implements Runnable {
6126        ParcelFileDescriptor mInputFile;
6127        String mCurrentPassword;
6128        String mDecryptPassword;
6129        IFullBackupRestoreObserver mObserver;
6130        AtomicBoolean mLatchObject;
6131        IBackupAgent mAgent;
6132        String mAgentPackage;
6133        ApplicationInfo mTargetApp;
6134        FullBackupObbConnection mObbConnection = null;
6135        ParcelFileDescriptor[] mPipes = null;
6136        byte[] mWidgetData = null;
6137
6138        long mBytes;
6139
6140        // possible handling states for a given package in the restore dataset
6141        final HashMap<String, RestorePolicy> mPackagePolicies
6142                = new HashMap<String, RestorePolicy>();
6143
6144        // installer package names for each encountered app, derived from the manifests
6145        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
6146
6147        // Signatures for a given package found in its manifest file
6148        final HashMap<String, Signature[]> mManifestSignatures
6149                = new HashMap<String, Signature[]>();
6150
6151        // Packages we've already wiped data on when restoring their first file
6152        final HashSet<String> mClearedPackages = new HashSet<String>();
6153
6154        PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
6155                IFullBackupRestoreObserver observer, AtomicBoolean latch) {
6156            mInputFile = fd;
6157            mCurrentPassword = curPassword;
6158            mDecryptPassword = decryptPassword;
6159            mObserver = observer;
6160            mLatchObject = latch;
6161            mAgent = null;
6162            mAgentPackage = null;
6163            mTargetApp = null;
6164            mObbConnection = new FullBackupObbConnection();
6165
6166            // Which packages we've already wiped data on.  We prepopulate this
6167            // with a whitelist of packages known to be unclearable.
6168            mClearedPackages.add("android");
6169            mClearedPackages.add(SETTINGS_PACKAGE);
6170        }
6171
6172        class RestoreFileRunnable implements Runnable {
6173            IBackupAgent mAgent;
6174            FileMetadata mInfo;
6175            ParcelFileDescriptor mSocket;
6176            int mToken;
6177
6178            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
6179                    ParcelFileDescriptor socket, int token) throws IOException {
6180                mAgent = agent;
6181                mInfo = info;
6182                mToken = token;
6183
6184                // This class is used strictly for process-local binder invocations.  The
6185                // semantics of ParcelFileDescriptor differ in this case; in particular, we
6186                // do not automatically get a 'dup'ed descriptor that we can can continue
6187                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
6188                // before proceeding to do the restore.
6189                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
6190            }
6191
6192            @Override
6193            public void run() {
6194                try {
6195                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
6196                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
6197                            mToken, mBackupManagerBinder);
6198                } catch (RemoteException e) {
6199                    // never happens; this is used strictly for local binder calls
6200                }
6201            }
6202        }
6203
6204        @Override
6205        public void run() {
6206            Slog.i(TAG, "--- Performing full-dataset restore ---");
6207            mObbConnection.establish();
6208            sendStartRestore();
6209
6210            // Are we able to restore shared-storage data?
6211            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
6212                mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
6213            }
6214
6215            FileInputStream rawInStream = null;
6216            DataInputStream rawDataIn = null;
6217            try {
6218                if (!backupPasswordMatches(mCurrentPassword)) {
6219                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
6220                    return;
6221                }
6222
6223                mBytes = 0;
6224                byte[] buffer = new byte[32 * 1024];
6225                rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
6226                rawDataIn = new DataInputStream(rawInStream);
6227
6228                // First, parse out the unencrypted/uncompressed header
6229                boolean compressed = false;
6230                InputStream preCompressStream = rawInStream;
6231                final InputStream in;
6232
6233                boolean okay = false;
6234                final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
6235                byte[] streamHeader = new byte[headerLen];
6236                rawDataIn.readFully(streamHeader);
6237                byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
6238                if (Arrays.equals(magicBytes, streamHeader)) {
6239                    // okay, header looks good.  now parse out the rest of the fields.
6240                    String s = readHeaderLine(rawInStream);
6241                    final int archiveVersion = Integer.parseInt(s);
6242                    if (archiveVersion <= BACKUP_FILE_VERSION) {
6243                        // okay, it's a version we recognize.  if it's version 1, we may need
6244                        // to try two different PBKDF2 regimes to compare checksums.
6245                        final boolean pbkdf2Fallback = (archiveVersion == 1);
6246
6247                        s = readHeaderLine(rawInStream);
6248                        compressed = (Integer.parseInt(s) != 0);
6249                        s = readHeaderLine(rawInStream);
6250                        if (s.equals("none")) {
6251                            // no more header to parse; we're good to go
6252                            okay = true;
6253                        } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
6254                            preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
6255                                    rawInStream);
6256                            if (preCompressStream != null) {
6257                                okay = true;
6258                            }
6259                        } else Slog.w(TAG, "Archive is encrypted but no password given");
6260                    } else Slog.w(TAG, "Wrong header version: " + s);
6261                } else Slog.w(TAG, "Didn't read the right header magic");
6262
6263                if (!okay) {
6264                    Slog.w(TAG, "Invalid restore data; aborting.");
6265                    return;
6266                }
6267
6268                // okay, use the right stream layer based on compression
6269                in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
6270
6271                boolean didRestore;
6272                do {
6273                    didRestore = restoreOneFile(in, buffer);
6274                } while (didRestore);
6275
6276                if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
6277            } catch (IOException e) {
6278                Slog.e(TAG, "Unable to read restore input");
6279            } finally {
6280                tearDownPipes();
6281                tearDownAgent(mTargetApp);
6282
6283                try {
6284                    if (rawDataIn != null) rawDataIn.close();
6285                    if (rawInStream != null) rawInStream.close();
6286                    mInputFile.close();
6287                } catch (IOException e) {
6288                    Slog.w(TAG, "Close of restore data pipe threw", e);
6289                    /* nothing we can do about this */
6290                }
6291                synchronized (mCurrentOpLock) {
6292                    mCurrentOperations.clear();
6293                }
6294                synchronized (mLatchObject) {
6295                    mLatchObject.set(true);
6296                    mLatchObject.notifyAll();
6297                }
6298                mObbConnection.tearDown();
6299                sendEndRestore();
6300                Slog.d(TAG, "Full restore pass complete.");
6301                mWakelock.release();
6302            }
6303        }
6304
6305        String readHeaderLine(InputStream in) throws IOException {
6306            int c;
6307            StringBuilder buffer = new StringBuilder(80);
6308            while ((c = in.read()) >= 0) {
6309                if (c == '\n') break;   // consume and discard the newlines
6310                buffer.append((char)c);
6311            }
6312            return buffer.toString();
6313        }
6314
6315        InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
6316                int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
6317                boolean doLog) {
6318            InputStream result = null;
6319
6320            try {
6321                Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
6322                SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt,
6323                        rounds);
6324                byte[] IV = hexToByteArray(userIvHex);
6325                IvParameterSpec ivSpec = new IvParameterSpec(IV);
6326                c.init(Cipher.DECRYPT_MODE,
6327                        new SecretKeySpec(userKey.getEncoded(), "AES"),
6328                        ivSpec);
6329                byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
6330                byte[] mkBlob = c.doFinal(mkCipher);
6331
6332                // first, the master key IV
6333                int offset = 0;
6334                int len = mkBlob[offset++];
6335                IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
6336                offset += len;
6337                // then the master key itself
6338                len = mkBlob[offset++];
6339                byte[] mk = Arrays.copyOfRange(mkBlob,
6340                        offset, offset + len);
6341                offset += len;
6342                // and finally the master key checksum hash
6343                len = mkBlob[offset++];
6344                byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
6345                        offset, offset + len);
6346
6347                // now validate the decrypted master key against the checksum
6348                byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds);
6349                if (Arrays.equals(calculatedCk, mkChecksum)) {
6350                    ivSpec = new IvParameterSpec(IV);
6351                    c.init(Cipher.DECRYPT_MODE,
6352                            new SecretKeySpec(mk, "AES"),
6353                            ivSpec);
6354                    // Only if all of the above worked properly will 'result' be assigned
6355                    result = new CipherInputStream(rawInStream, c);
6356                } else if (doLog) Slog.w(TAG, "Incorrect password");
6357            } catch (InvalidAlgorithmParameterException e) {
6358                if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e);
6359            } catch (BadPaddingException e) {
6360                // This case frequently occurs when the wrong password is used to decrypt
6361                // the master key.  Use the identical "incorrect password" log text as is
6362                // used in the checksum failure log in order to avoid providing additional
6363                // information to an attacker.
6364                if (doLog) Slog.w(TAG, "Incorrect password");
6365            } catch (IllegalBlockSizeException e) {
6366                if (doLog) Slog.w(TAG, "Invalid block size in master key");
6367            } catch (NoSuchAlgorithmException e) {
6368                if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!");
6369            } catch (NoSuchPaddingException e) {
6370                if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!");
6371            } catch (InvalidKeyException e) {
6372                if (doLog) Slog.w(TAG, "Illegal password; aborting");
6373            }
6374
6375            return result;
6376        }
6377
6378        InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
6379                InputStream rawInStream) {
6380            InputStream result = null;
6381            try {
6382                if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
6383
6384                    String userSaltHex = readHeaderLine(rawInStream); // 5
6385                    byte[] userSalt = hexToByteArray(userSaltHex);
6386
6387                    String ckSaltHex = readHeaderLine(rawInStream); // 6
6388                    byte[] ckSalt = hexToByteArray(ckSaltHex);
6389
6390                    int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
6391                    String userIvHex = readHeaderLine(rawInStream); // 8
6392
6393                    String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
6394
6395                    // decrypt the master key blob
6396                    result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt,
6397                            rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
6398                    if (result == null && pbkdf2Fallback) {
6399                        result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt,
6400                                rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
6401                    }
6402                } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
6403            } catch (NumberFormatException e) {
6404                Slog.w(TAG, "Can't parse restore data header");
6405            } catch (IOException e) {
6406                Slog.w(TAG, "Can't read input header");
6407            }
6408
6409            return result;
6410        }
6411
6412        boolean restoreOneFile(InputStream instream, byte[] buffer) {
6413            FileMetadata info;
6414            try {
6415                info = readTarHeaders(instream);
6416                if (info != null) {
6417                    if (MORE_DEBUG) {
6418                        dumpFileMetadata(info);
6419                    }
6420
6421                    final String pkg = info.packageName;
6422                    if (!pkg.equals(mAgentPackage)) {
6423                        // okay, change in package; set up our various
6424                        // bookkeeping if we haven't seen it yet
6425                        if (!mPackagePolicies.containsKey(pkg)) {
6426                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6427                        }
6428
6429                        // Clean up the previous agent relationship if necessary,
6430                        // and let the observer know we're considering a new app.
6431                        if (mAgent != null) {
6432                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
6433                            // Now we're really done
6434                            tearDownPipes();
6435                            tearDownAgent(mTargetApp);
6436                            mTargetApp = null;
6437                            mAgentPackage = null;
6438                        }
6439                    }
6440
6441                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
6442                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
6443                        mPackageInstallers.put(pkg, info.installerPackageName);
6444                        // We've read only the manifest content itself at this point,
6445                        // so consume the footer before looping around to the next
6446                        // input file
6447                        skipTarPadding(info.size, instream);
6448                        sendOnRestorePackage(pkg);
6449                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
6450                        // Metadata blobs!
6451                        readMetadata(info, instream);
6452                        skipTarPadding(info.size, instream);
6453                    } else {
6454                        // Non-manifest, so it's actual file data.  Is this a package
6455                        // we're ignoring?
6456                        boolean okay = true;
6457                        RestorePolicy policy = mPackagePolicies.get(pkg);
6458                        switch (policy) {
6459                            case IGNORE:
6460                                okay = false;
6461                                break;
6462
6463                            case ACCEPT_IF_APK:
6464                                // If we're in accept-if-apk state, then the first file we
6465                                // see MUST be the apk.
6466                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6467                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
6468                                    // Try to install the app.
6469                                    String installerName = mPackageInstallers.get(pkg);
6470                                    okay = installApk(info, installerName, instream);
6471                                    // good to go; promote to ACCEPT
6472                                    mPackagePolicies.put(pkg, (okay)
6473                                            ? RestorePolicy.ACCEPT
6474                                            : RestorePolicy.IGNORE);
6475                                    // At this point we've consumed this file entry
6476                                    // ourselves, so just strip the tar footer and
6477                                    // go on to the next file in the input stream
6478                                    skipTarPadding(info.size, instream);
6479                                    return true;
6480                                } else {
6481                                    // File data before (or without) the apk.  We can't
6482                                    // handle it coherently in this case so ignore it.
6483                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6484                                    okay = false;
6485                                }
6486                                break;
6487
6488                            case ACCEPT:
6489                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6490                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
6491                                    // we can take the data without the apk, so we
6492                                    // *want* to do so.  skip the apk by declaring this
6493                                    // one file not-okay without changing the restore
6494                                    // policy for the package.
6495                                    okay = false;
6496                                }
6497                                break;
6498
6499                            default:
6500                                // Something has gone dreadfully wrong when determining
6501                                // the restore policy from the manifest.  Ignore the
6502                                // rest of this package's data.
6503                                Slog.e(TAG, "Invalid policy from manifest");
6504                                okay = false;
6505                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6506                                break;
6507                        }
6508
6509                        // The path needs to be canonical
6510                        if (info.path.contains("..") || info.path.contains("//")) {
6511                            if (MORE_DEBUG) {
6512                                Slog.w(TAG, "Dropping invalid path " + info.path);
6513                            }
6514                            okay = false;
6515                        }
6516
6517                        // If the policy is satisfied, go ahead and set up to pipe the
6518                        // data to the agent.
6519                        if (DEBUG && okay && mAgent != null) {
6520                            Slog.i(TAG, "Reusing existing agent instance");
6521                        }
6522                        if (okay && mAgent == null) {
6523                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
6524
6525                            try {
6526                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
6527
6528                                // If we haven't sent any data to this app yet, we probably
6529                                // need to clear it first.  Check that.
6530                                if (!mClearedPackages.contains(pkg)) {
6531                                    // apps with their own backup agents are
6532                                    // responsible for coherently managing a full
6533                                    // restore.
6534                                    if (mTargetApp.backupAgentName == null) {
6535                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
6536                                        clearApplicationDataSynchronous(pkg);
6537                                    } else {
6538                                        if (DEBUG) Slog.d(TAG, "backup agent ("
6539                                                + mTargetApp.backupAgentName + ") => no clear");
6540                                    }
6541                                    mClearedPackages.add(pkg);
6542                                } else {
6543                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
6544                                }
6545
6546                                // All set; now set up the IPC and launch the agent
6547                                setUpPipes();
6548                                mAgent = bindToAgentSynchronous(mTargetApp,
6549                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
6550                                mAgentPackage = pkg;
6551                            } catch (IOException e) {
6552                                // fall through to error handling
6553                            } catch (NameNotFoundException e) {
6554                                // fall through to error handling
6555                            }
6556
6557                            if (mAgent == null) {
6558                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
6559                                okay = false;
6560                                tearDownPipes();
6561                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6562                            }
6563                        }
6564
6565                        // Sanity check: make sure we never give data to the wrong app.  This
6566                        // should never happen but a little paranoia here won't go amiss.
6567                        if (okay && !pkg.equals(mAgentPackage)) {
6568                            Slog.e(TAG, "Restoring data for " + pkg
6569                                    + " but agent is for " + mAgentPackage);
6570                            okay = false;
6571                        }
6572
6573                        // At this point we have an agent ready to handle the full
6574                        // restore data as well as a pipe for sending data to
6575                        // that agent.  Tell the agent to start reading from the
6576                        // pipe.
6577                        if (okay) {
6578                            boolean agentSuccess = true;
6579                            long toCopy = info.size;
6580                            final int token = generateToken();
6581                            try {
6582                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
6583                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
6584                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
6585                                            + " : " + info.path);
6586                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
6587                                            info.size, info.type, info.path, info.mode,
6588                                            info.mtime, token, mBackupManagerBinder);
6589                                } else {
6590                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
6591                                            + info.path);
6592                                    // fire up the app's agent listening on the socket.  If
6593                                    // the agent is running in the system process we can't
6594                                    // just invoke it asynchronously, so we provide a thread
6595                                    // for it here.
6596                                    if (mTargetApp.processName.equals("system")) {
6597                                        Slog.d(TAG, "system process agent - spinning a thread");
6598                                        RestoreFileRunnable runner = new RestoreFileRunnable(
6599                                                mAgent, info, mPipes[0], token);
6600                                        new Thread(runner, "restore-sys-runner").start();
6601                                    } else {
6602                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
6603                                                info.domain, info.path, info.mode, info.mtime,
6604                                                token, mBackupManagerBinder);
6605                                    }
6606                                }
6607                            } catch (IOException e) {
6608                                // couldn't dup the socket for a process-local restore
6609                                Slog.d(TAG, "Couldn't establish restore");
6610                                agentSuccess = false;
6611                                okay = false;
6612                            } catch (RemoteException e) {
6613                                // whoops, remote entity went away.  We'll eat the content
6614                                // ourselves, then, and not copy it over.
6615                                Slog.e(TAG, "Agent crashed during full restore");
6616                                agentSuccess = false;
6617                                okay = false;
6618                            }
6619
6620                            // Copy over the data if the agent is still good
6621                            if (okay) {
6622                                boolean pipeOkay = true;
6623                                FileOutputStream pipe = new FileOutputStream(
6624                                        mPipes[1].getFileDescriptor());
6625                                while (toCopy > 0) {
6626                                    int toRead = (toCopy > buffer.length)
6627                                    ? buffer.length : (int)toCopy;
6628                                    int nRead = instream.read(buffer, 0, toRead);
6629                                    if (nRead >= 0) mBytes += nRead;
6630                                    if (nRead <= 0) break;
6631                                    toCopy -= nRead;
6632
6633                                    // send it to the output pipe as long as things
6634                                    // are still good
6635                                    if (pipeOkay) {
6636                                        try {
6637                                            pipe.write(buffer, 0, nRead);
6638                                        } catch (IOException e) {
6639                                            Slog.e(TAG, "Failed to write to restore pipe", e);
6640                                            pipeOkay = false;
6641                                        }
6642                                    }
6643                                }
6644
6645                                // done sending that file!  Now we just need to consume
6646                                // the delta from info.size to the end of block.
6647                                skipTarPadding(info.size, instream);
6648
6649                                // and now that we've sent it all, wait for the remote
6650                                // side to acknowledge receipt
6651                                agentSuccess = waitUntilOperationComplete(token);
6652                            }
6653
6654                            // okay, if the remote end failed at any point, deal with
6655                            // it by ignoring the rest of the restore on it
6656                            if (!agentSuccess) {
6657                                mBackupHandler.removeMessages(MSG_TIMEOUT);
6658                                tearDownPipes();
6659                                tearDownAgent(mTargetApp);
6660                                mAgent = null;
6661                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6662                            }
6663                        }
6664
6665                        // Problems setting up the agent communication, or an already-
6666                        // ignored package: skip to the next tar stream entry by
6667                        // reading and discarding this file.
6668                        if (!okay) {
6669                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
6670                            long bytesToConsume = (info.size + 511) & ~511;
6671                            while (bytesToConsume > 0) {
6672                                int toRead = (bytesToConsume > buffer.length)
6673                                ? buffer.length : (int)bytesToConsume;
6674                                long nRead = instream.read(buffer, 0, toRead);
6675                                if (nRead >= 0) mBytes += nRead;
6676                                if (nRead <= 0) break;
6677                                bytesToConsume -= nRead;
6678                            }
6679                        }
6680                    }
6681                }
6682            } catch (IOException e) {
6683                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
6684                // treat as EOF
6685                info = null;
6686            }
6687
6688            return (info != null);
6689        }
6690
6691        void setUpPipes() throws IOException {
6692            mPipes = ParcelFileDescriptor.createPipe();
6693        }
6694
6695        void tearDownPipes() {
6696            if (mPipes != null) {
6697                try {
6698                    mPipes[0].close();
6699                    mPipes[0] = null;
6700                    mPipes[1].close();
6701                    mPipes[1] = null;
6702                } catch (IOException e) {
6703                    Slog.w(TAG, "Couldn't close agent pipes", e);
6704                }
6705                mPipes = null;
6706            }
6707        }
6708
6709        void tearDownAgent(ApplicationInfo app) {
6710            if (mAgent != null) {
6711                try {
6712                    // unbind and tidy up even on timeout or failure, just in case
6713                    mActivityManager.unbindBackupAgent(app);
6714
6715                    // The agent was running with a stub Application object, so shut it down.
6716                    // !!! We hardcode the confirmation UI's package name here rather than use a
6717                    //     manifest flag!  TODO something less direct.
6718                    if (app.uid != Process.SYSTEM_UID
6719                            && !app.packageName.equals("com.android.backupconfirm")) {
6720                        if (DEBUG) Slog.d(TAG, "Killing host process");
6721                        mActivityManager.killApplicationProcess(app.processName, app.uid);
6722                    } else {
6723                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
6724                    }
6725                } catch (RemoteException e) {
6726                    Slog.d(TAG, "Lost app trying to shut down");
6727                }
6728                mAgent = null;
6729            }
6730        }
6731
6732        class RestoreInstallObserver extends PackageInstallObserver {
6733            final AtomicBoolean mDone = new AtomicBoolean();
6734            String mPackageName;
6735            int mResult;
6736
6737            public void reset() {
6738                synchronized (mDone) {
6739                    mDone.set(false);
6740                }
6741            }
6742
6743            public void waitForCompletion() {
6744                synchronized (mDone) {
6745                    while (mDone.get() == false) {
6746                        try {
6747                            mDone.wait();
6748                        } catch (InterruptedException e) { }
6749                    }
6750                }
6751            }
6752
6753            int getResult() {
6754                return mResult;
6755            }
6756
6757            @Override
6758            public void onPackageInstalled(String packageName, int returnCode,
6759                    String msg, Bundle extras) {
6760                synchronized (mDone) {
6761                    mResult = returnCode;
6762                    mPackageName = packageName;
6763                    mDone.set(true);
6764                    mDone.notifyAll();
6765                }
6766            }
6767        }
6768
6769        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
6770            final AtomicBoolean mDone = new AtomicBoolean();
6771            int mResult;
6772
6773            public void reset() {
6774                synchronized (mDone) {
6775                    mDone.set(false);
6776                }
6777            }
6778
6779            public void waitForCompletion() {
6780                synchronized (mDone) {
6781                    while (mDone.get() == false) {
6782                        try {
6783                            mDone.wait();
6784                        } catch (InterruptedException e) { }
6785                    }
6786                }
6787            }
6788
6789            @Override
6790            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
6791                synchronized (mDone) {
6792                    mResult = returnCode;
6793                    mDone.set(true);
6794                    mDone.notifyAll();
6795                }
6796            }
6797        }
6798
6799        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
6800        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
6801
6802        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
6803            boolean okay = true;
6804
6805            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
6806
6807            // The file content is an .apk file.  Copy it out to a staging location and
6808            // attempt to install it.
6809            File apkFile = new File(mDataDir, info.packageName);
6810            try {
6811                FileOutputStream apkStream = new FileOutputStream(apkFile);
6812                byte[] buffer = new byte[32 * 1024];
6813                long size = info.size;
6814                while (size > 0) {
6815                    long toRead = (buffer.length < size) ? buffer.length : size;
6816                    int didRead = instream.read(buffer, 0, (int)toRead);
6817                    if (didRead >= 0) mBytes += didRead;
6818                    apkStream.write(buffer, 0, didRead);
6819                    size -= didRead;
6820                }
6821                apkStream.close();
6822
6823                // make sure the installer can read it
6824                apkFile.setReadable(true, false);
6825
6826                // Now install it
6827                Uri packageUri = Uri.fromFile(apkFile);
6828                mInstallObserver.reset();
6829                mPackageManager.installPackage(packageUri, mInstallObserver,
6830                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
6831                        installerPackage);
6832                mInstallObserver.waitForCompletion();
6833
6834                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
6835                    // The only time we continue to accept install of data even if the
6836                    // apk install failed is if we had already determined that we could
6837                    // accept the data regardless.
6838                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
6839                        okay = false;
6840                    }
6841                } else {
6842                    // Okay, the install succeeded.  Make sure it was the right app.
6843                    boolean uninstall = false;
6844                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
6845                        Slog.w(TAG, "Restore stream claimed to include apk for "
6846                                + info.packageName + " but apk was really "
6847                                + mInstallObserver.mPackageName);
6848                        // delete the package we just put in place; it might be fraudulent
6849                        okay = false;
6850                        uninstall = true;
6851                    } else {
6852                        try {
6853                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
6854                                    PackageManager.GET_SIGNATURES);
6855                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
6856                                Slog.w(TAG, "Restore stream contains apk of package "
6857                                        + info.packageName + " but it disallows backup/restore");
6858                                okay = false;
6859                            } else {
6860                                // So far so good -- do the signatures match the manifest?
6861                                Signature[] sigs = mManifestSignatures.get(info.packageName);
6862                                if (signaturesMatch(sigs, pkg)) {
6863                                    // If this is a system-uid app without a declared backup agent,
6864                                    // don't restore any of the file data.
6865                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
6866                                            && (pkg.applicationInfo.backupAgentName == null)) {
6867                                        Slog.w(TAG, "Installed app " + info.packageName
6868                                                + " has restricted uid and no agent");
6869                                        okay = false;
6870                                    }
6871                                } else {
6872                                    Slog.w(TAG, "Installed app " + info.packageName
6873                                            + " signatures do not match restore manifest");
6874                                    okay = false;
6875                                    uninstall = true;
6876                                }
6877                            }
6878                        } catch (NameNotFoundException e) {
6879                            Slog.w(TAG, "Install of package " + info.packageName
6880                                    + " succeeded but now not found");
6881                            okay = false;
6882                        }
6883                    }
6884
6885                    // If we're not okay at this point, we need to delete the package
6886                    // that we just installed.
6887                    if (uninstall) {
6888                        mDeleteObserver.reset();
6889                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
6890                                mDeleteObserver, 0);
6891                        mDeleteObserver.waitForCompletion();
6892                    }
6893                }
6894            } catch (IOException e) {
6895                Slog.e(TAG, "Unable to transcribe restored apk for install");
6896                okay = false;
6897            } finally {
6898                apkFile.delete();
6899            }
6900
6901            return okay;
6902        }
6903
6904        // Given an actual file content size, consume the post-content padding mandated
6905        // by the tar format.
6906        void skipTarPadding(long size, InputStream instream) throws IOException {
6907            long partial = (size + 512) % 512;
6908            if (partial > 0) {
6909                final int needed = 512 - (int)partial;
6910                byte[] buffer = new byte[needed];
6911                if (readExactly(instream, buffer, 0, needed) == needed) {
6912                    mBytes += needed;
6913                } else throw new IOException("Unexpected EOF in padding");
6914            }
6915        }
6916
6917        // Read a widget metadata file, returning the restored blob
6918        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
6919            // Fail on suspiciously large widget dump files
6920            if (info.size > 64 * 1024) {
6921                throw new IOException("Metadata too big; corrupt? size=" + info.size);
6922            }
6923
6924            byte[] buffer = new byte[(int) info.size];
6925            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6926                mBytes += info.size;
6927            } else throw new IOException("Unexpected EOF in widget data");
6928
6929            String[] str = new String[1];
6930            int offset = extractLine(buffer, 0, str);
6931            int version = Integer.parseInt(str[0]);
6932            if (version == BACKUP_MANIFEST_VERSION) {
6933                offset = extractLine(buffer, offset, str);
6934                final String pkg = str[0];
6935                if (info.packageName.equals(pkg)) {
6936                    // Data checks out -- the rest of the buffer is a concatenation of
6937                    // binary blobs as described in the comment at writeAppWidgetData()
6938                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
6939                            offset, buffer.length - offset);
6940                    DataInputStream in = new DataInputStream(bin);
6941                    while (bin.available() > 0) {
6942                        int token = in.readInt();
6943                        int size = in.readInt();
6944                        if (size > 64 * 1024) {
6945                            throw new IOException("Datum "
6946                                    + Integer.toHexString(token)
6947                                    + " too big; corrupt? size=" + info.size);
6948                        }
6949                        switch (token) {
6950                            case BACKUP_WIDGET_METADATA_TOKEN:
6951                            {
6952                                if (MORE_DEBUG) {
6953                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
6954                                }
6955                                mWidgetData = new byte[size];
6956                                in.read(mWidgetData);
6957                                break;
6958                            }
6959                            default:
6960                            {
6961                                if (DEBUG) {
6962                                    Slog.i(TAG, "Ignoring metadata blob "
6963                                            + Integer.toHexString(token)
6964                                            + " for " + info.packageName);
6965                                }
6966                                in.skipBytes(size);
6967                                break;
6968                            }
6969                        }
6970                    }
6971                } else {
6972                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
6973                            + " but widget data for " + pkg);
6974                }
6975            } else {
6976                Slog.w(TAG, "Unsupported metadata version " + version);
6977            }
6978        }
6979
6980        // Returns a policy constant; takes a buffer arg to reduce memory churn
6981        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
6982                throws IOException {
6983            // Fail on suspiciously large manifest files
6984            if (info.size > 64 * 1024) {
6985                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
6986            }
6987
6988            byte[] buffer = new byte[(int) info.size];
6989            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6990                mBytes += info.size;
6991            } else throw new IOException("Unexpected EOF in manifest");
6992
6993            RestorePolicy policy = RestorePolicy.IGNORE;
6994            String[] str = new String[1];
6995            int offset = 0;
6996
6997            try {
6998                offset = extractLine(buffer, offset, str);
6999                int version = Integer.parseInt(str[0]);
7000                if (version == BACKUP_MANIFEST_VERSION) {
7001                    offset = extractLine(buffer, offset, str);
7002                    String manifestPackage = str[0];
7003                    // TODO: handle <original-package>
7004                    if (manifestPackage.equals(info.packageName)) {
7005                        offset = extractLine(buffer, offset, str);
7006                        version = Integer.parseInt(str[0]);  // app version
7007                        offset = extractLine(buffer, offset, str);
7008                        // This is the platform version, which we don't use, but we parse it
7009                        // as a safety against corruption in the manifest.
7010                        Integer.parseInt(str[0]);
7011                        offset = extractLine(buffer, offset, str);
7012                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
7013                        offset = extractLine(buffer, offset, str);
7014                        boolean hasApk = str[0].equals("1");
7015                        offset = extractLine(buffer, offset, str);
7016                        int numSigs = Integer.parseInt(str[0]);
7017                        if (numSigs > 0) {
7018                            Signature[] sigs = new Signature[numSigs];
7019                            for (int i = 0; i < numSigs; i++) {
7020                                offset = extractLine(buffer, offset, str);
7021                                sigs[i] = new Signature(str[0]);
7022                            }
7023                            mManifestSignatures.put(info.packageName, sigs);
7024
7025                            // Okay, got the manifest info we need...
7026                            try {
7027                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
7028                                        info.packageName, PackageManager.GET_SIGNATURES);
7029                                // Fall through to IGNORE if the app explicitly disallows backup
7030                                final int flags = pkgInfo.applicationInfo.flags;
7031                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
7032                                    // Restore system-uid-space packages only if they have
7033                                    // defined a custom backup agent
7034                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
7035                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
7036                                        // Verify signatures against any installed version; if they
7037                                        // don't match, then we fall though and ignore the data.  The
7038                                        // signatureMatch() method explicitly ignores the signature
7039                                        // check for packages installed on the system partition, because
7040                                        // such packages are signed with the platform cert instead of
7041                                        // the app developer's cert, so they're different on every
7042                                        // device.
7043                                        if (signaturesMatch(sigs, pkgInfo)) {
7044                                            if (pkgInfo.versionCode >= version) {
7045                                                Slog.i(TAG, "Sig + version match; taking data");
7046                                                policy = RestorePolicy.ACCEPT;
7047                                            } else {
7048                                                // The data is from a newer version of the app than
7049                                                // is presently installed.  That means we can only
7050                                                // use it if the matching apk is also supplied.
7051                                                Slog.d(TAG, "Data version " + version
7052                                                        + " is newer than installed version "
7053                                                        + pkgInfo.versionCode + " - requiring apk");
7054                                                policy = RestorePolicy.ACCEPT_IF_APK;
7055                                            }
7056                                        } else {
7057                                            Slog.w(TAG, "Restore manifest signatures do not match "
7058                                                    + "installed application for " + info.packageName);
7059                                        }
7060                                    } else {
7061                                        Slog.w(TAG, "Package " + info.packageName
7062                                                + " is system level with no agent");
7063                                    }
7064                                } else {
7065                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
7066                                            + info.packageName + " but allowBackup=false");
7067                                }
7068                            } catch (NameNotFoundException e) {
7069                                // Okay, the target app isn't installed.  We can process
7070                                // the restore properly only if the dataset provides the
7071                                // apk file and we can successfully install it.
7072                                if (DEBUG) Slog.i(TAG, "Package " + info.packageName
7073                                        + " not installed; requiring apk in dataset");
7074                                policy = RestorePolicy.ACCEPT_IF_APK;
7075                            }
7076
7077                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
7078                                Slog.i(TAG, "Cannot restore package " + info.packageName
7079                                        + " without the matching .apk");
7080                            }
7081                        } else {
7082                            Slog.i(TAG, "Missing signature on backed-up package "
7083                                    + info.packageName);
7084                        }
7085                    } else {
7086                        Slog.i(TAG, "Expected package " + info.packageName
7087                                + " but restore manifest claims " + manifestPackage);
7088                    }
7089                } else {
7090                    Slog.i(TAG, "Unknown restore manifest version " + version
7091                            + " for package " + info.packageName);
7092                }
7093            } catch (NumberFormatException e) {
7094                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
7095            } catch (IllegalArgumentException e) {
7096                Slog.w(TAG, e.getMessage());
7097            }
7098
7099            return policy;
7100        }
7101
7102        // Builds a line from a byte buffer starting at 'offset', and returns
7103        // the index of the next unconsumed data in the buffer.
7104        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
7105            final int end = buffer.length;
7106            if (offset >= end) throw new IOException("Incomplete data");
7107
7108            int pos;
7109            for (pos = offset; pos < end; pos++) {
7110                byte c = buffer[pos];
7111                // at LF we declare end of line, and return the next char as the
7112                // starting point for the next time through
7113                if (c == '\n') {
7114                    break;
7115                }
7116            }
7117            outStr[0] = new String(buffer, offset, pos - offset);
7118            pos++;  // may be pointing an extra byte past the end but that's okay
7119            return pos;
7120        }
7121
7122        void dumpFileMetadata(FileMetadata info) {
7123            if (DEBUG) {
7124                StringBuilder b = new StringBuilder(128);
7125
7126                // mode string
7127                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
7128                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
7129                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
7130                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
7131                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
7132                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
7133                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
7134                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
7135                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
7136                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
7137                b.append(String.format(" %9d ", info.size));
7138
7139                Date stamp = new Date(info.mtime);
7140                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
7141
7142                b.append(info.packageName);
7143                b.append(" :: ");
7144                b.append(info.domain);
7145                b.append(" :: ");
7146                b.append(info.path);
7147
7148                Slog.i(TAG, b.toString());
7149            }
7150        }
7151        // Consume a tar file header block [sequence] and accumulate the relevant metadata
7152        FileMetadata readTarHeaders(InputStream instream) throws IOException {
7153            byte[] block = new byte[512];
7154            FileMetadata info = null;
7155
7156            boolean gotHeader = readTarHeader(instream, block);
7157            if (gotHeader) {
7158                try {
7159                    // okay, presume we're okay, and extract the various metadata
7160                    info = new FileMetadata();
7161                    info.size = extractRadix(block, 124, 12, 8);
7162                    info.mtime = extractRadix(block, 136, 12, 8);
7163                    info.mode = extractRadix(block, 100, 8, 8);
7164
7165                    info.path = extractString(block, 345, 155); // prefix
7166                    String path = extractString(block, 0, 100);
7167                    if (path.length() > 0) {
7168                        if (info.path.length() > 0) info.path += '/';
7169                        info.path += path;
7170                    }
7171
7172                    // tar link indicator field: 1 byte at offset 156 in the header.
7173                    int typeChar = block[156];
7174                    if (typeChar == 'x') {
7175                        // pax extended header, so we need to read that
7176                        gotHeader = readPaxExtendedHeader(instream, info);
7177                        if (gotHeader) {
7178                            // and after a pax extended header comes another real header -- read
7179                            // that to find the real file type
7180                            gotHeader = readTarHeader(instream, block);
7181                        }
7182                        if (!gotHeader) throw new IOException("Bad or missing pax header");
7183
7184                        typeChar = block[156];
7185                    }
7186
7187                    switch (typeChar) {
7188                        case '0': info.type = BackupAgent.TYPE_FILE; break;
7189                        case '5': {
7190                            info.type = BackupAgent.TYPE_DIRECTORY;
7191                            if (info.size != 0) {
7192                                Slog.w(TAG, "Directory entry with nonzero size in header");
7193                                info.size = 0;
7194                            }
7195                            break;
7196                        }
7197                        case 0: {
7198                            // presume EOF
7199                            if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
7200                            return null;
7201                        }
7202                        default: {
7203                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
7204                            throw new IOException("Unknown entity type " + typeChar);
7205                        }
7206                    }
7207
7208                    // Parse out the path
7209                    //
7210                    // first: apps/shared/unrecognized
7211                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
7212                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
7213                        // File in shared storage.  !!! TODO: implement this.
7214                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
7215                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
7216                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
7217                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
7218                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
7219                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
7220                        // App content!  Parse out the package name and domain
7221
7222                        // strip the apps/ prefix
7223                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
7224
7225                        // extract the package name
7226                        int slash = info.path.indexOf('/');
7227                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
7228                        info.packageName = info.path.substring(0, slash);
7229                        info.path = info.path.substring(slash+1);
7230
7231                        // if it's a manifest or metadata payload we're done, otherwise parse
7232                        // out the domain into which the file will be restored
7233                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
7234                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
7235                            slash = info.path.indexOf('/');
7236                            if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
7237                            info.domain = info.path.substring(0, slash);
7238                            info.path = info.path.substring(slash + 1);
7239                        }
7240                    }
7241                } catch (IOException e) {
7242                    if (DEBUG) {
7243                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
7244                        HEXLOG(block);
7245                    }
7246                    throw e;
7247                }
7248            }
7249            return info;
7250        }
7251
7252        private void HEXLOG(byte[] block) {
7253            int offset = 0;
7254            int todo = block.length;
7255            StringBuilder buf = new StringBuilder(64);
7256            while (todo > 0) {
7257                buf.append(String.format("%04x   ", offset));
7258                int numThisLine = (todo > 16) ? 16 : todo;
7259                for (int i = 0; i < numThisLine; i++) {
7260                    buf.append(String.format("%02x ", block[offset+i]));
7261                }
7262                Slog.i("hexdump", buf.toString());
7263                buf.setLength(0);
7264                todo -= numThisLine;
7265                offset += numThisLine;
7266            }
7267        }
7268
7269        // Read exactly the given number of bytes into a buffer at the stated offset.
7270        // Returns false if EOF is encountered before the requested number of bytes
7271        // could be read.
7272        int readExactly(InputStream in, byte[] buffer, int offset, int size)
7273                throws IOException {
7274            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
7275
7276            int soFar = 0;
7277            while (soFar < size) {
7278                int nRead = in.read(buffer, offset + soFar, size - soFar);
7279                if (nRead <= 0) {
7280                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
7281                    break;
7282                }
7283                soFar += nRead;
7284            }
7285            return soFar;
7286        }
7287
7288        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
7289            final int got = readExactly(instream, block, 0, 512);
7290            if (got == 0) return false;     // Clean EOF
7291            if (got < 512) throw new IOException("Unable to read full block header");
7292            mBytes += 512;
7293            return true;
7294        }
7295
7296        // overwrites 'info' fields based on the pax extended header
7297        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
7298                throws IOException {
7299            // We should never see a pax extended header larger than this
7300            if (info.size > 32*1024) {
7301                Slog.w(TAG, "Suspiciously large pax header size " + info.size
7302                        + " - aborting");
7303                throw new IOException("Sanity failure: pax header size " + info.size);
7304            }
7305
7306            // read whole blocks, not just the content size
7307            int numBlocks = (int)((info.size + 511) >> 9);
7308            byte[] data = new byte[numBlocks * 512];
7309            if (readExactly(instream, data, 0, data.length) < data.length) {
7310                throw new IOException("Unable to read full pax header");
7311            }
7312            mBytes += data.length;
7313
7314            final int contentSize = (int) info.size;
7315            int offset = 0;
7316            do {
7317                // extract the line at 'offset'
7318                int eol = offset+1;
7319                while (eol < contentSize && data[eol] != ' ') eol++;
7320                if (eol >= contentSize) {
7321                    // error: we just hit EOD looking for the end of the size field
7322                    throw new IOException("Invalid pax data");
7323                }
7324                // eol points to the space between the count and the key
7325                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
7326                int key = eol + 1;  // start of key=value
7327                eol = offset + linelen - 1; // trailing LF
7328                int value;
7329                for (value = key+1; data[value] != '=' && value <= eol; value++);
7330                if (value > eol) {
7331                    throw new IOException("Invalid pax declaration");
7332                }
7333
7334                // pax requires that key/value strings be in UTF-8
7335                String keyStr = new String(data, key, value-key, "UTF-8");
7336                // -1 to strip the trailing LF
7337                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
7338
7339                if ("path".equals(keyStr)) {
7340                    info.path = valStr;
7341                } else if ("size".equals(keyStr)) {
7342                    info.size = Long.parseLong(valStr);
7343                } else {
7344                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
7345                }
7346
7347                offset += linelen;
7348            } while (offset < contentSize);
7349
7350            return true;
7351        }
7352
7353        long extractRadix(byte[] data, int offset, int maxChars, int radix)
7354                throws IOException {
7355            long value = 0;
7356            final int end = offset + maxChars;
7357            for (int i = offset; i < end; i++) {
7358                final byte b = data[i];
7359                // Numeric fields in tar can terminate with either NUL or SPC
7360                if (b == 0 || b == ' ') break;
7361                if (b < '0' || b > ('0' + radix - 1)) {
7362                    throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix);
7363                }
7364                value = radix * value + (b - '0');
7365            }
7366            return value;
7367        }
7368
7369        String extractString(byte[] data, int offset, int maxChars) throws IOException {
7370            final int end = offset + maxChars;
7371            int eos = offset;
7372            // tar string fields terminate early with a NUL
7373            while (eos < end && data[eos] != 0) eos++;
7374            return new String(data, offset, eos-offset, "US-ASCII");
7375        }
7376
7377        void sendStartRestore() {
7378            if (mObserver != null) {
7379                try {
7380                    mObserver.onStartRestore();
7381                } catch (RemoteException e) {
7382                    Slog.w(TAG, "full restore observer went away: startRestore");
7383                    mObserver = null;
7384                }
7385            }
7386        }
7387
7388        void sendOnRestorePackage(String name) {
7389            if (mObserver != null) {
7390                try {
7391                    // TODO: use a more user-friendly name string
7392                    mObserver.onRestorePackage(name);
7393                } catch (RemoteException e) {
7394                    Slog.w(TAG, "full restore observer went away: restorePackage");
7395                    mObserver = null;
7396                }
7397            }
7398        }
7399
7400        void sendEndRestore() {
7401            if (mObserver != null) {
7402                try {
7403                    mObserver.onEndRestore();
7404                } catch (RemoteException e) {
7405                    Slog.w(TAG, "full restore observer went away: endRestore");
7406                    mObserver = null;
7407                }
7408            }
7409        }
7410    }
7411
7412    // ----- Restore handling -----
7413
7414    // new style: we only store the SHA-1 hashes of each sig, not the full block
7415    static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
7416        if (target == null) {
7417            return false;
7418        }
7419
7420        // If the target resides on the system partition, we allow it to restore
7421        // data from the like-named package in a restore set even if the signatures
7422        // do not match.  (Unlike general applications, those flashed to the system
7423        // partition will be signed with the device's platform certificate, so on
7424        // different phones the same system app will have different signatures.)
7425        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
7426            if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
7427            return true;
7428        }
7429
7430        // Allow unsigned apps, but not signed on one device and unsigned on the other
7431        // !!! TODO: is this the right policy?
7432        Signature[] deviceSigs = target.signatures;
7433        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
7434                + " device=" + deviceSigs);
7435        if ((storedSigHashes == null || storedSigHashes.size() == 0)
7436                && (deviceSigs == null || deviceSigs.length == 0)) {
7437            return true;
7438        }
7439        if (storedSigHashes == null || deviceSigs == null) {
7440            return false;
7441        }
7442
7443        // !!! TODO: this demands that every stored signature match one
7444        // that is present on device, and does not demand the converse.
7445        // Is this this right policy?
7446        final int nStored = storedSigHashes.size();
7447        final int nDevice = deviceSigs.length;
7448
7449        // hash each on-device signature
7450        ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
7451        for (int i = 0; i < nDevice; i++) {
7452            deviceHashes.add(hashSignature(deviceSigs[i]));
7453        }
7454
7455        // now ensure that each stored sig (hash) matches an on-device sig (hash)
7456        for (int n = 0; n < nStored; n++) {
7457            boolean match = false;
7458            final byte[] storedHash = storedSigHashes.get(n);
7459            for (int i = 0; i < nDevice; i++) {
7460                if (Arrays.equals(storedHash, deviceHashes.get(i))) {
7461                    match = true;
7462                    break;
7463                }
7464            }
7465            // match is false when no on-device sig matched one of the stored ones
7466            if (!match) {
7467                return false;
7468            }
7469        }
7470
7471        return true;
7472    }
7473
7474    static byte[] hashSignature(Signature sig) {
7475        try {
7476            MessageDigest digest = MessageDigest.getInstance("SHA-256");
7477            digest.update(sig.toByteArray());
7478            return digest.digest();
7479        } catch (NoSuchAlgorithmException e) {
7480            Slog.w(TAG, "No SHA-256 algorithm found!");
7481        }
7482        return null;
7483    }
7484
7485    // Old style: directly match the stored vs on device signature blocks
7486    static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
7487        if (target == null) {
7488            return false;
7489        }
7490
7491        // If the target resides on the system partition, we allow it to restore
7492        // data from the like-named package in a restore set even if the signatures
7493        // do not match.  (Unlike general applications, those flashed to the system
7494        // partition will be signed with the device's platform certificate, so on
7495        // different phones the same system app will have different signatures.)
7496        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
7497            if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
7498            return true;
7499        }
7500
7501        // Allow unsigned apps, but not signed on one device and unsigned on the other
7502        // !!! TODO: is this the right policy?
7503        Signature[] deviceSigs = target.signatures;
7504        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
7505                + " device=" + deviceSigs);
7506        if ((storedSigs == null || storedSigs.length == 0)
7507                && (deviceSigs == null || deviceSigs.length == 0)) {
7508            return true;
7509        }
7510        if (storedSigs == null || deviceSigs == null) {
7511            return false;
7512        }
7513
7514        // !!! TODO: this demands that every stored signature match one
7515        // that is present on device, and does not demand the converse.
7516        // Is this this right policy?
7517        int nStored = storedSigs.length;
7518        int nDevice = deviceSigs.length;
7519
7520        for (int i=0; i < nStored; i++) {
7521            boolean match = false;
7522            for (int j=0; j < nDevice; j++) {
7523                if (storedSigs[i].equals(deviceSigs[j])) {
7524                    match = true;
7525                    break;
7526                }
7527            }
7528            if (!match) {
7529                return false;
7530            }
7531        }
7532        return true;
7533    }
7534
7535    // Used by both incremental and full restore
7536    void restoreWidgetData(String packageName, byte[] widgetData) {
7537        // Apply the restored widget state and generate the ID update for the app
7538        // TODO: http://b/22388012
7539        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
7540    }
7541
7542    // *****************************
7543    // NEW UNIFIED RESTORE IMPLEMENTATION
7544    // *****************************
7545
7546    // states of the unified-restore state machine
7547    enum UnifiedRestoreState {
7548        INITIAL,
7549        RUNNING_QUEUE,
7550        RESTORE_KEYVALUE,
7551        RESTORE_FULL,
7552        RESTORE_FINISHED,
7553        FINAL
7554    }
7555
7556    class PerformUnifiedRestoreTask implements BackupRestoreTask {
7557        // Transport we're working with to do the restore
7558        private IBackupTransport mTransport;
7559
7560        // Where per-transport saved state goes
7561        File mStateDir;
7562
7563        // Restore observer; may be null
7564        private IRestoreObserver mObserver;
7565
7566        // Token identifying the dataset to the transport
7567        private long mToken;
7568
7569        // When this is a restore-during-install, this is the token identifying the
7570        // operation to the Package Manager, and we must ensure that we let it know
7571        // when we're finished.
7572        private int mPmToken;
7573
7574        // Is this a whole-system restore, i.e. are we establishing a new ancestral
7575        // dataset to base future restore-at-install operations from?
7576        private boolean mIsSystemRestore;
7577
7578        // If this is a single-package restore, what package are we interested in?
7579        private PackageInfo mTargetPackage;
7580
7581        // In all cases, the calculated list of packages that we are trying to restore
7582        private List<PackageInfo> mAcceptSet;
7583
7584        // Our bookkeeping about the ancestral dataset
7585        private PackageManagerBackupAgent mPmAgent;
7586
7587        // Currently-bound backup agent for restore + restoreFinished purposes
7588        private IBackupAgent mAgent;
7589
7590        // What sort of restore we're doing now
7591        private RestoreDescription mRestoreDescription;
7592
7593        // The package we're currently restoring
7594        private PackageInfo mCurrentPackage;
7595
7596        // Widget-related data handled as part of this restore operation
7597        private byte[] mWidgetData;
7598
7599        // Number of apps restored in this pass
7600        private int mCount;
7601
7602        // When did we start?
7603        private long mStartRealtime;
7604
7605        // State machine progress
7606        private UnifiedRestoreState mState;
7607
7608        // How are things going?
7609        private int mStatus;
7610
7611        // Done?
7612        private boolean mFinished;
7613
7614        // Key/value: bookkeeping about staged data and files for agent access
7615        private File mBackupDataName;
7616        private File mStageName;
7617        private File mSavedStateName;
7618        private File mNewStateName;
7619        ParcelFileDescriptor mBackupData;
7620        ParcelFileDescriptor mNewState;
7621
7622        // Invariant: mWakelock is already held, and this task is responsible for
7623        // releasing it at the end of the restore operation.
7624        PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer,
7625                long restoreSetToken, PackageInfo targetPackage, int pmToken,
7626                boolean isFullSystemRestore, String[] filterSet) {
7627            mState = UnifiedRestoreState.INITIAL;
7628            mStartRealtime = SystemClock.elapsedRealtime();
7629
7630            mTransport = transport;
7631            mObserver = observer;
7632            mToken = restoreSetToken;
7633            mPmToken = pmToken;
7634            mTargetPackage = targetPackage;
7635            mIsSystemRestore = isFullSystemRestore;
7636            mFinished = false;
7637
7638            if (targetPackage != null) {
7639                // Single package restore
7640                mAcceptSet = new ArrayList<PackageInfo>();
7641                mAcceptSet.add(targetPackage);
7642            } else {
7643                // Everything possible, or a target set
7644                if (filterSet == null) {
7645                    // We want everything and a pony
7646                    List<PackageInfo> apps =
7647                            PackageManagerBackupAgent.getStorableApplications(mPackageManager);
7648                    filterSet = packagesToNames(apps);
7649                    if (DEBUG) {
7650                        Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
7651                    }
7652                }
7653
7654                mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
7655
7656                // Pro tem, we insist on moving the settings provider package to last place.
7657                // Keep track of whether it's in the list, and bump it down if so.  We also
7658                // want to do the system package itself first if it's called for.
7659                boolean hasSystem = false;
7660                boolean hasSettings = false;
7661                for (int i = 0; i < filterSet.length; i++) {
7662                    try {
7663                        PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
7664                        if ("android".equals(info.packageName)) {
7665                            hasSystem = true;
7666                            continue;
7667                        }
7668                        if (SETTINGS_PACKAGE.equals(info.packageName)) {
7669                            hasSettings = true;
7670                            continue;
7671                        }
7672
7673                        if (appIsEligibleForBackup(info.applicationInfo)) {
7674                            mAcceptSet.add(info);
7675                        }
7676                    } catch (NameNotFoundException e) {
7677                        // requested package name doesn't exist; ignore it
7678                    }
7679                }
7680                if (hasSystem) {
7681                    try {
7682                        mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
7683                    } catch (NameNotFoundException e) {
7684                        // won't happen; we know a priori that it's valid
7685                    }
7686                }
7687                if (hasSettings) {
7688                    try {
7689                        mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
7690                    } catch (NameNotFoundException e) {
7691                        // this one is always valid too
7692                    }
7693                }
7694            }
7695
7696            if (MORE_DEBUG) {
7697                Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
7698                for (PackageInfo info : mAcceptSet) {
7699                    Slog.v(TAG, "   " + info.packageName);
7700                }
7701            }
7702        }
7703
7704        private String[] packagesToNames(List<PackageInfo> apps) {
7705            final int N = apps.size();
7706            String[] names = new String[N];
7707            for (int i = 0; i < N; i++) {
7708                names[i] = apps.get(i).packageName;
7709            }
7710            return names;
7711        }
7712
7713        // Execute one tick of whatever state machine the task implements
7714        @Override
7715        public void execute() {
7716            if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState);
7717            switch (mState) {
7718                case INITIAL:
7719                    startRestore();
7720                    break;
7721
7722                case RUNNING_QUEUE:
7723                    dispatchNextRestore();
7724                    break;
7725
7726                case RESTORE_KEYVALUE:
7727                    restoreKeyValue();
7728                    break;
7729
7730                case RESTORE_FULL:
7731                    restoreFull();
7732                    break;
7733
7734                case RESTORE_FINISHED:
7735                    restoreFinished();
7736                    break;
7737
7738                case FINAL:
7739                    if (!mFinished) finalizeRestore();
7740                    else {
7741                        Slog.e(TAG, "Duplicate finish");
7742                    }
7743                    mFinished = true;
7744                    break;
7745            }
7746        }
7747
7748        /*
7749         * SKETCH OF OPERATION
7750         *
7751         * create one of these PerformUnifiedRestoreTask objects, telling it which
7752         * dataset & transport to address, and then parameters within the restore
7753         * operation: single target package vs many, etc.
7754         *
7755         * 1. transport.startRestore(token, list-of-packages).  If we need @pm@  it is
7756         * always placed first and the settings provider always placed last [for now].
7757         *
7758         * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline]
7759         *
7760         *   [ state change => RUNNING_QUEUE ]
7761         *
7762         * NOW ITERATE:
7763         *
7764         * { 3. t.nextRestorePackage()
7765         *   4. does the metadata for this package allow us to restore it?
7766         *      does the on-disk app permit us to restore it? [re-check allowBackup etc]
7767         *   5. is this a key/value dataset?  => key/value agent restore
7768         *       [ state change => RESTORE_KEYVALUE ]
7769         *       5a. spin up agent
7770         *       5b. t.getRestoreData() to stage it properly
7771         *       5c. call into agent to perform restore
7772         *       5d. tear down agent
7773         *       [ state change => RUNNING_QUEUE ]
7774         *
7775         *   6. else it's a stream dataset:
7776         *       [ state change => RESTORE_FULL ]
7777         *       6a. instantiate the engine for a stream restore: engine handles agent lifecycles
7778         *       6b. spin off engine runner on separate thread
7779         *       6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket
7780         *       [ state change => RUNNING_QUEUE ]
7781         * }
7782         *
7783         *   [ state change => FINAL ]
7784         *
7785         * 7. t.finishRestore(), release wakelock, etc.
7786         *
7787         *
7788         */
7789
7790        // state INITIAL : set up for the restore and read the metadata if necessary
7791        private  void startRestore() {
7792            sendStartRestore(mAcceptSet.size());
7793
7794            // If we're starting a full-system restore, set up to begin widget ID remapping
7795            if (mIsSystemRestore) {
7796                // TODO: http://b/22388012
7797                AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM);
7798            }
7799
7800            try {
7801                String transportDir = mTransport.transportDirName();
7802                mStateDir = new File(mBaseStateDir, transportDir);
7803
7804                // Fetch the current metadata from the dataset first
7805                PackageInfo pmPackage = new PackageInfo();
7806                pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7807                mAcceptSet.add(0, pmPackage);
7808
7809                PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
7810                mStatus = mTransport.startRestore(mToken, packages);
7811                if (mStatus != BackupTransport.TRANSPORT_OK) {
7812                    Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
7813                    mStatus = BackupTransport.TRANSPORT_ERROR;
7814                    executeNextState(UnifiedRestoreState.FINAL);
7815                    return;
7816                }
7817
7818                RestoreDescription desc = mTransport.nextRestorePackage();
7819                if (desc == null) {
7820                    Slog.e(TAG, "No restore metadata available; halting");
7821                    mStatus = BackupTransport.TRANSPORT_ERROR;
7822                    executeNextState(UnifiedRestoreState.FINAL);
7823                    return;
7824                }
7825                if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
7826                    Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
7827                    mStatus = BackupTransport.TRANSPORT_ERROR;
7828                    executeNextState(UnifiedRestoreState.FINAL);
7829                    return;
7830                }
7831
7832                // Pull the Package Manager metadata from the restore set first
7833                mCurrentPackage = new PackageInfo();
7834                mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7835                mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
7836                mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
7837                if (MORE_DEBUG) {
7838                    Slog.v(TAG, "initiating restore for PMBA");
7839                }
7840                initiateOneRestore(mCurrentPackage, 0);
7841                // The PM agent called operationComplete() already, because our invocation
7842                // of it is process-local and therefore synchronous.  That means that the
7843                // next-state message (RUNNING_QUEUE) is already enqueued.  Only if we're
7844                // unable to proceed with running the queue do we remove that pending
7845                // message and jump straight to the FINAL state.  Because this was
7846                // synchronous we also know that we should cancel the pending timeout
7847                // message.
7848                mBackupHandler.removeMessages(MSG_TIMEOUT);
7849
7850                // Verify that the backup set includes metadata.  If not, we can't do
7851                // signature/version verification etc, so we simply do not proceed with
7852                // the restore operation.
7853                if (!mPmAgent.hasMetadata()) {
7854                    Slog.e(TAG, "No restore metadata available, so not restoring");
7855                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7856                            PACKAGE_MANAGER_SENTINEL,
7857                            "Package manager restore metadata missing");
7858                    mStatus = BackupTransport.TRANSPORT_ERROR;
7859                    mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7860                    executeNextState(UnifiedRestoreState.FINAL);
7861                    return;
7862                }
7863
7864                // Success; cache the metadata and continue as expected with the
7865                // next state already enqueued
7866
7867            } catch (RemoteException e) {
7868                // If we lost the transport at any time, halt
7869                Slog.e(TAG, "Unable to contact transport for restore");
7870                mStatus = BackupTransport.TRANSPORT_ERROR;
7871                mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7872                executeNextState(UnifiedRestoreState.FINAL);
7873                return;
7874            }
7875        }
7876
7877        // state RUNNING_QUEUE : figure out what the next thing to be restored is,
7878        // and fire the appropriate next step
7879        private void dispatchNextRestore() {
7880            UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
7881            try {
7882                mRestoreDescription = mTransport.nextRestorePackage();
7883                final String pkgName = (mRestoreDescription != null)
7884                        ? mRestoreDescription.getPackageName() : null;
7885                if (pkgName == null) {
7886                    Slog.e(TAG, "Failure getting next package name");
7887                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7888                    nextState = UnifiedRestoreState.FINAL;
7889                    return;
7890                } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) {
7891                    // Yay we've reached the end cleanly
7892                    if (DEBUG) {
7893                        Slog.v(TAG, "No more packages; finishing restore");
7894                    }
7895                    int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
7896                    EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
7897                    nextState = UnifiedRestoreState.FINAL;
7898                    return;
7899                }
7900
7901                if (DEBUG) {
7902                    Slog.i(TAG, "Next restore package: " + mRestoreDescription);
7903                }
7904                sendOnRestorePackage(pkgName);
7905
7906                Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName);
7907                if (metaInfo == null) {
7908                    Slog.e(TAG, "No metadata for " + pkgName);
7909                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
7910                            "Package metadata missing");
7911                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7912                    return;
7913                }
7914
7915                try {
7916                    mCurrentPackage = mPackageManager.getPackageInfo(
7917                            pkgName, PackageManager.GET_SIGNATURES);
7918                } catch (NameNotFoundException e) {
7919                    // Whoops, we thought we could restore this package but it
7920                    // turns out not to be present.  Skip it.
7921                    Slog.e(TAG, "Package not present: " + pkgName);
7922                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
7923                            "Package missing on device");
7924                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7925                    return;
7926                }
7927
7928                if (metaInfo.versionCode > mCurrentPackage.versionCode) {
7929                    // Data is from a "newer" version of the app than we have currently
7930                    // installed.  If the app has not declared that it is prepared to
7931                    // handle this case, we do not attempt the restore.
7932                    if ((mCurrentPackage.applicationInfo.flags
7933                            & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
7934                        String message = "Version " + metaInfo.versionCode
7935                                + " > installed version " + mCurrentPackage.versionCode;
7936                        Slog.w(TAG, "Package " + pkgName + ": " + message);
7937                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7938                                pkgName, message);
7939                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
7940                        return;
7941                    } else {
7942                        if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
7943                                + " > installed " + mCurrentPackage.versionCode
7944                                + " but restoreAnyVersion");
7945                    }
7946                }
7947
7948                if (MORE_DEBUG) Slog.v(TAG, "Package " + pkgName
7949                        + " restore version [" + metaInfo.versionCode
7950                        + "] is compatible with installed version ["
7951                        + mCurrentPackage.versionCode + "]");
7952
7953                // Reset per-package preconditions and fire the appropriate next state
7954                mWidgetData = null;
7955                final int type = mRestoreDescription.getDataType();
7956                if (type == RestoreDescription.TYPE_KEY_VALUE) {
7957                    nextState = UnifiedRestoreState.RESTORE_KEYVALUE;
7958                } else if (type == RestoreDescription.TYPE_FULL_STREAM) {
7959                    nextState = UnifiedRestoreState.RESTORE_FULL;
7960                } else {
7961                    // Unknown restore type; ignore this package and move on
7962                    Slog.e(TAG, "Unrecognized restore type " + type);
7963                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7964                    return;
7965                }
7966            } catch (RemoteException e) {
7967                Slog.e(TAG, "Can't get next target from transport; ending restore");
7968                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7969                nextState = UnifiedRestoreState.FINAL;
7970                return;
7971            } finally {
7972                executeNextState(nextState);
7973            }
7974        }
7975
7976        // state RESTORE_KEYVALUE : restore one package via key/value API set
7977        private void restoreKeyValue() {
7978            // Initiating the restore will pass responsibility for the state machine's
7979            // progress to the agent callback, so we do not always execute the
7980            // next state here.
7981            final String packageName = mCurrentPackage.packageName;
7982            // Validate some semantic requirements that apply in this way
7983            // only to the key/value restore API flow
7984            if (mCurrentPackage.applicationInfo.backupAgentName == null
7985                    || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
7986                if (MORE_DEBUG) {
7987                    Slog.i(TAG, "Data exists for package " + packageName
7988                            + " but app has no agent; skipping");
7989                }
7990                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7991                        "Package has no agent");
7992                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7993                return;
7994            }
7995
7996            Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
7997            if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
7998                Slog.w(TAG, "Signature mismatch restoring " + packageName);
7999                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8000                        "Signature mismatch");
8001                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8002                return;
8003            }
8004
8005            // Good to go!  Set up and bind the agent...
8006            mAgent = bindToAgentSynchronous(
8007                    mCurrentPackage.applicationInfo,
8008                    IApplicationThread.BACKUP_MODE_INCREMENTAL);
8009            if (mAgent == null) {
8010                Slog.w(TAG, "Can't find backup agent for " + packageName);
8011                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
8012                        "Restore agent missing");
8013                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8014                return;
8015            }
8016
8017            // And then finally start the restore on this agent
8018            try {
8019                initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
8020                ++mCount;
8021            } catch (Exception e) {
8022                Slog.e(TAG, "Error when attempting restore: " + e.toString());
8023                keyValueAgentErrorCleanup();
8024                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8025            }
8026        }
8027
8028        // Guts of a key/value restore operation
8029        void initiateOneRestore(PackageInfo app, int appVersionCode) {
8030            final String packageName = app.packageName;
8031
8032            if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
8033
8034            // !!! TODO: get the dirs from the transport
8035            mBackupDataName = new File(mDataDir, packageName + ".restore");
8036            mStageName = new File(mDataDir, packageName + ".stage");
8037            mNewStateName = new File(mStateDir, packageName + ".new");
8038            mSavedStateName = new File(mStateDir, packageName);
8039
8040            // don't stage the 'android' package where the wallpaper data lives.  this is
8041            // an optimization: we know there's no widget data hosted/published by that
8042            // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
8043            // data following the download.
8044            boolean staging = !packageName.equals("android");
8045            ParcelFileDescriptor stage;
8046            File downloadFile = (staging) ? mStageName : mBackupDataName;
8047
8048            final int token = generateToken();
8049            try {
8050                // Run the transport's restore pass
8051                stage = ParcelFileDescriptor.open(downloadFile,
8052                        ParcelFileDescriptor.MODE_READ_WRITE |
8053                        ParcelFileDescriptor.MODE_CREATE |
8054                        ParcelFileDescriptor.MODE_TRUNCATE);
8055
8056                if (!SELinux.restorecon(mBackupDataName)) {
8057                    if (MORE_DEBUG) Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
8058                }
8059
8060                if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
8061                    // Transport-level failure, so we wind everything up and
8062                    // terminate the restore operation.
8063                    Slog.e(TAG, "Error getting restore data for " + packageName);
8064                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8065                    stage.close();
8066                    downloadFile.delete();
8067                    executeNextState(UnifiedRestoreState.FINAL);
8068                    return;
8069                }
8070
8071                // We have the data from the transport. Now we extract and strip
8072                // any per-package metadata (typically widget-related information)
8073                // if appropriate
8074                if (staging) {
8075                    stage.close();
8076                    stage = ParcelFileDescriptor.open(downloadFile,
8077                            ParcelFileDescriptor.MODE_READ_ONLY);
8078
8079                    mBackupData = ParcelFileDescriptor.open(mBackupDataName,
8080                            ParcelFileDescriptor.MODE_READ_WRITE |
8081                            ParcelFileDescriptor.MODE_CREATE |
8082                            ParcelFileDescriptor.MODE_TRUNCATE);
8083
8084                    BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
8085                    BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
8086                    byte[] buffer = new byte[8192]; // will grow when needed
8087                    while (in.readNextHeader()) {
8088                        final String key = in.getKey();
8089                        final int size = in.getDataSize();
8090
8091                        // is this a special key?
8092                        if (key.equals(KEY_WIDGET_STATE)) {
8093                            if (DEBUG) {
8094                                Slog.i(TAG, "Restoring widget state for " + packageName);
8095                            }
8096                            mWidgetData = new byte[size];
8097                            in.readEntityData(mWidgetData, 0, size);
8098                        } else {
8099                            if (size > buffer.length) {
8100                                buffer = new byte[size];
8101                            }
8102                            in.readEntityData(buffer, 0, size);
8103                            out.writeEntityHeader(key, size);
8104                            out.writeEntityData(buffer, size);
8105                        }
8106                    }
8107
8108                    mBackupData.close();
8109                }
8110
8111                // Okay, we have the data.  Now have the agent do the restore.
8112                stage.close();
8113                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
8114                        ParcelFileDescriptor.MODE_READ_ONLY);
8115
8116                mNewState = ParcelFileDescriptor.open(mNewStateName,
8117                        ParcelFileDescriptor.MODE_READ_WRITE |
8118                        ParcelFileDescriptor.MODE_CREATE |
8119                        ParcelFileDescriptor.MODE_TRUNCATE);
8120
8121                // Kick off the restore, checking for hung agents.  The timeout or
8122                // the operationComplete() callback will schedule the next step,
8123                // so we do not do that here.
8124                prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
8125                mAgent.doRestore(mBackupData, appVersionCode, mNewState,
8126                        token, mBackupManagerBinder);
8127            } catch (Exception e) {
8128                Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
8129                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8130                        packageName, e.toString());
8131                keyValueAgentErrorCleanup();    // clears any pending timeout messages as well
8132
8133                // After a restore failure we go back to running the queue.  If there
8134                // are no more packages to be restored that will be handled by the
8135                // next step.
8136                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8137            }
8138        }
8139
8140        // state RESTORE_FULL : restore one package via streaming engine
8141        private void restoreFull() {
8142            // None of this can run on the work looper here, so we spin asynchronous
8143            // work like this:
8144            //
8145            //   StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk()
8146            //                       write it into the pipe to the engine
8147            //   EngineThread: FullRestoreEngine thread communicating with the target app
8148            //
8149            // When finished, StreamFeederThread executes next state as appropriate on the
8150            // backup looper, and the overall unified restore task resumes
8151            try {
8152                StreamFeederThread feeder = new StreamFeederThread();
8153                if (MORE_DEBUG) {
8154                    Slog.i(TAG, "Spinning threads for stream restore of "
8155                            + mCurrentPackage.packageName);
8156                }
8157                new Thread(feeder, "unified-stream-feeder").start();
8158
8159                // At this point the feeder is responsible for advancing the restore
8160                // state, so we're done here.
8161            } catch (IOException e) {
8162                // Unable to instantiate the feeder thread -- we need to bail on the
8163                // current target.  We haven't asked the transport for data yet, though,
8164                // so we can do that simply by going back to running the restore queue.
8165                Slog.e(TAG, "Unable to construct pipes for stream restore!");
8166                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8167            }
8168        }
8169
8170        // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
8171        private void restoreFinished() {
8172            try {
8173                final int token = generateToken();
8174                prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
8175                mAgent.doRestoreFinished(token, mBackupManagerBinder);
8176                // If we get this far, the callback or timeout will schedule the
8177                // next restore state, so we're done
8178            } catch (Exception e) {
8179                final String packageName = mCurrentPackage.packageName;
8180                Slog.e(TAG, "Unable to finalize restore of " + packageName);
8181                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8182                        packageName, e.toString());
8183                keyValueAgentErrorCleanup();
8184                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8185            }
8186        }
8187
8188        class StreamFeederThread extends RestoreEngine implements Runnable {
8189            final String TAG = "StreamFeederThread";
8190            FullRestoreEngine mEngine;
8191
8192            // pipe through which we read data from the transport. [0] read, [1] write
8193            ParcelFileDescriptor[] mTransportPipes;
8194
8195            // pipe through which the engine will read data.  [0] read, [1] write
8196            ParcelFileDescriptor[] mEnginePipes;
8197
8198            public StreamFeederThread() throws IOException {
8199                mTransportPipes = ParcelFileDescriptor.createPipe();
8200                mEnginePipes = ParcelFileDescriptor.createPipe();
8201                setRunning(true);
8202            }
8203
8204            @Override
8205            public void run() {
8206                UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
8207                int status = BackupTransport.TRANSPORT_OK;
8208
8209                EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
8210                        mCurrentPackage.packageName);
8211
8212                mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false);
8213                EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]);
8214
8215                ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
8216                ParcelFileDescriptor tReadEnd = mTransportPipes[0];
8217                ParcelFileDescriptor tWriteEnd = mTransportPipes[1];
8218
8219                int bufferSize = 32 * 1024;
8220                byte[] buffer = new byte[bufferSize];
8221                FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor());
8222                FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor());
8223
8224                // spin up the engine and start moving data to it
8225                new Thread(eThread, "unified-restore-engine").start();
8226
8227                try {
8228                    while (status == BackupTransport.TRANSPORT_OK) {
8229                        // have the transport write some of the restoring data to us
8230                        int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd);
8231                        if (result > 0) {
8232                            // The transport wrote this many bytes of restore data to the
8233                            // pipe, so pass it along to the engine.
8234                            if (MORE_DEBUG) {
8235                                Slog.v(TAG, "  <- transport provided chunk size " + result);
8236                            }
8237                            if (result > bufferSize) {
8238                                bufferSize = result;
8239                                buffer = new byte[bufferSize];
8240                            }
8241                            int toCopy = result;
8242                            while (toCopy > 0) {
8243                                int n = transportIn.read(buffer, 0, toCopy);
8244                                engineOut.write(buffer, 0, n);
8245                                toCopy -= n;
8246                                if (MORE_DEBUG) {
8247                                    Slog.v(TAG, "  -> wrote " + n + " to engine, left=" + toCopy);
8248                                }
8249                            }
8250                        } else if (result == BackupTransport.NO_MORE_DATA) {
8251                            // Clean finish.  Wind up and we're done!
8252                            if (MORE_DEBUG) {
8253                                Slog.i(TAG, "Got clean full-restore EOF for "
8254                                        + mCurrentPackage.packageName);
8255                            }
8256                            status = BackupTransport.TRANSPORT_OK;
8257                            break;
8258                        } else {
8259                            // Transport reported some sort of failure; the fall-through
8260                            // handling will deal properly with that.
8261                            Slog.e(TAG, "Error " + result + " streaming restore for "
8262                                    + mCurrentPackage.packageName);
8263                            EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8264                            status = result;
8265                        }
8266                    }
8267                    if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through");
8268                } catch (IOException e) {
8269                    // We lost our ability to communicate via the pipes.  That's worrying
8270                    // but potentially recoverable; abandon this package's restore but
8271                    // carry on with the next restore target.
8272                    Slog.e(TAG, "Unable to route data for restore");
8273                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8274                            mCurrentPackage.packageName, "I/O error on pipes");
8275                    status = BackupTransport.AGENT_ERROR;
8276                } catch (RemoteException e) {
8277                    // The transport went away; terminate the whole operation.  Closing
8278                    // the sockets will wake up the engine and it will then tidy up the
8279                    // remote end.
8280                    Slog.e(TAG, "Transport failed during restore");
8281                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
8282                    status = BackupTransport.TRANSPORT_ERROR;
8283                } finally {
8284                    // Close the transport pipes and *our* end of the engine pipe,
8285                    // but leave the engine thread's end open so that it properly
8286                    // hits EOF and winds up its operations.
8287                    IoUtils.closeQuietly(mEnginePipes[1]);
8288                    IoUtils.closeQuietly(mTransportPipes[0]);
8289                    IoUtils.closeQuietly(mTransportPipes[1]);
8290
8291                    // Don't proceed until the engine has finished
8292                    eThread.waitForResult();
8293
8294                    if (MORE_DEBUG) {
8295                        Slog.i(TAG, "engine thread finished; proceeding");
8296                    }
8297
8298                    // Now we're really done with this one too
8299                    IoUtils.closeQuietly(mEnginePipes[0]);
8300
8301                    // If we hit a transport-level error, we are done with everything;
8302                    // if we hit an agent error we just go back to running the queue.
8303                    if (status == BackupTransport.TRANSPORT_OK) {
8304                        // Clean finish means we issue the restore-finished callback
8305                        nextState = UnifiedRestoreState.RESTORE_FINISHED;
8306
8307                        // the engine bound the target's agent, so recover that binding
8308                        // to use for the callback.
8309                        mAgent = mEngine.getAgent();
8310                    } else {
8311                        // Something went wrong somewhere.  Whether it was at the transport
8312                        // level is immaterial; we need to tell the transport to bail
8313                        try {
8314                            mTransport.abortFullRestore();
8315                        } catch (RemoteException e) {
8316                            // transport itself is dead; make sure we handle this as a
8317                            // fatal error
8318                            status = BackupTransport.TRANSPORT_ERROR;
8319                        }
8320
8321                        // We also need to wipe the current target's data, as it's probably
8322                        // in an incoherent state.
8323                        clearApplicationDataSynchronous(mCurrentPackage.packageName);
8324
8325                        // Schedule the next state based on the nature of our failure
8326                        if (status == BackupTransport.TRANSPORT_ERROR) {
8327                            nextState = UnifiedRestoreState.FINAL;
8328                        } else {
8329                            nextState = UnifiedRestoreState.RUNNING_QUEUE;
8330                        }
8331                    }
8332                    executeNextState(nextState);
8333                    setRunning(false);
8334                }
8335            }
8336
8337        }
8338
8339        class EngineThread implements Runnable {
8340            FullRestoreEngine mEngine;
8341            FileInputStream mEngineStream;
8342
8343            EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
8344                mEngine = engine;
8345                engine.setRunning(true);
8346                mEngineStream = new FileInputStream(engineSocket.getFileDescriptor());
8347            }
8348
8349            public boolean isRunning() {
8350                return mEngine.isRunning();
8351            }
8352
8353            public int waitForResult() {
8354                return mEngine.waitForResult();
8355            }
8356
8357            @Override
8358            public void run() {
8359                while (mEngine.isRunning()) {
8360                    // Tell it to be sure to leave the agent instance up after finishing
8361                    mEngine.restoreOneFile(mEngineStream, false);
8362                }
8363            }
8364        }
8365
8366        // state FINAL : tear everything down and we're done.
8367        private void finalizeRestore() {
8368            if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
8369
8370            try {
8371                mTransport.finishRestore();
8372            } catch (Exception e) {
8373                Slog.e(TAG, "Error finishing restore", e);
8374            }
8375
8376            // Tell the observer we're done
8377            if (mObserver != null) {
8378                try {
8379                    mObserver.restoreFinished(mStatus);
8380                } catch (RemoteException e) {
8381                    Slog.d(TAG, "Restore observer died at restoreFinished");
8382                }
8383            }
8384
8385            // Clear any ongoing session timeout.
8386            mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
8387
8388            // If we have a PM token, we must under all circumstances be sure to
8389            // handshake when we've finished.
8390            if (mPmToken > 0) {
8391                if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
8392                try {
8393                    mPackageManagerBinder.finishPackageInstall(mPmToken);
8394                } catch (RemoteException e) { /* can't happen */ }
8395            } else {
8396                // We were invoked via an active restore session, not by the Package
8397                // Manager, so start up the session timeout again.
8398                mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
8399                        TIMEOUT_RESTORE_INTERVAL);
8400            }
8401
8402            // Kick off any work that may be needed regarding app widget restores
8403            // TODO: http://b/22388012
8404            AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM);
8405
8406            // If this was a full-system restore, record the ancestral
8407            // dataset information
8408            if (mIsSystemRestore && mPmAgent != null) {
8409                mAncestralPackages = mPmAgent.getRestoredPackages();
8410                mAncestralToken = mToken;
8411                writeRestoreTokens();
8412            }
8413
8414            // done; we can finally release the wakelock and be legitimately done.
8415            Slog.i(TAG, "Restore complete.");
8416            mWakelock.release();
8417        }
8418
8419        void keyValueAgentErrorCleanup() {
8420            // If the agent fails restore, it might have put the app's data
8421            // into an incoherent state.  For consistency we wipe its data
8422            // again in this case before continuing with normal teardown
8423            clearApplicationDataSynchronous(mCurrentPackage.packageName);
8424            keyValueAgentCleanup();
8425        }
8426
8427        // TODO: clean up naming; this is now used at finish by both k/v and stream restores
8428        void keyValueAgentCleanup() {
8429            mBackupDataName.delete();
8430            mStageName.delete();
8431            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
8432            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
8433            mBackupData = mNewState = null;
8434
8435            // if everything went okay, remember the recorded state now
8436            //
8437            // !!! TODO: the restored data could be migrated on the server
8438            // side into the current dataset.  In that case the new state file
8439            // we just created would reflect the data already extant in the
8440            // backend, so there'd be nothing more to do.  Until that happens,
8441            // however, we need to make sure that we record the data to the
8442            // current backend dataset.  (Yes, this means shipping the data over
8443            // the wire in both directions.  That's bad, but consistency comes
8444            // first, then efficiency.)  Once we introduce server-side data
8445            // migration to the newly-restored device's dataset, we will change
8446            // the following from a discard of the newly-written state to the
8447            // "correct" operation of renaming into the canonical state blob.
8448            mNewStateName.delete();                      // TODO: remove; see above comment
8449            //mNewStateName.renameTo(mSavedStateName);   // TODO: replace with this
8450
8451            // If this wasn't the PM pseudopackage, tear down the agent side
8452            if (mCurrentPackage.applicationInfo != null) {
8453                // unbind and tidy up even on timeout or failure
8454                try {
8455                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
8456
8457                    // The agent was probably running with a stub Application object,
8458                    // which isn't a valid run mode for the main app logic.  Shut
8459                    // down the app so that next time it's launched, it gets the
8460                    // usual full initialization.  Note that this is only done for
8461                    // full-system restores: when a single app has requested a restore,
8462                    // it is explicitly not killed following that operation.
8463                    //
8464                    // We execute this kill when these conditions hold:
8465                    //    1. the app did not request its own restore (mTargetPackage == null), and either
8466                    //    2a. the app is a full-data target (TYPE_FULL_STREAM) or
8467                    //     b. the app does not state android:killAfterRestore="false" in its manifest
8468                    final int appFlags = mCurrentPackage.applicationInfo.flags;
8469                    final boolean killAfterRestore =
8470                            (mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM)
8471                            || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0);
8472
8473                    if (mTargetPackage == null && killAfterRestore) {
8474                        if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
8475                                + mCurrentPackage.applicationInfo.processName);
8476                        mActivityManager.killApplicationProcess(
8477                                mCurrentPackage.applicationInfo.processName,
8478                                mCurrentPackage.applicationInfo.uid);
8479                    }
8480                } catch (RemoteException e) {
8481                    // can't happen; we run in the same process as the activity manager
8482                }
8483            }
8484
8485            // The caller is responsible for reestablishing the state machine; our
8486            // responsibility here is to clear the decks for whatever comes next.
8487            mBackupHandler.removeMessages(MSG_TIMEOUT, this);
8488            synchronized (mCurrentOpLock) {
8489                mCurrentOperations.clear();
8490            }
8491        }
8492
8493        @Override
8494        public void operationComplete(int unusedResult) {
8495            if (MORE_DEBUG) {
8496                Slog.i(TAG, "operationComplete() during restore: target="
8497                        + mCurrentPackage.packageName
8498                        + " state=" + mState);
8499            }
8500
8501            final UnifiedRestoreState nextState;
8502            switch (mState) {
8503                case INITIAL:
8504                    // We've just (manually) restored the PMBA.  It doesn't need the
8505                    // additional restore-finished callback so we bypass that and go
8506                    // directly to running the queue.
8507                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8508                    break;
8509
8510                case RESTORE_KEYVALUE:
8511                case RESTORE_FULL: {
8512                    // Okay, we've just heard back from the agent that it's done with
8513                    // the restore itself.  We now have to send the same agent its
8514                    // doRestoreFinished() callback, so roll into that state.
8515                    nextState = UnifiedRestoreState.RESTORE_FINISHED;
8516                    break;
8517                }
8518
8519                case RESTORE_FINISHED: {
8520                    // Okay, we're done with this package.  Tidy up and go on to the next
8521                    // app in the queue.
8522                    int size = (int) mBackupDataName.length();
8523                    EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
8524                            mCurrentPackage.packageName, size);
8525
8526                    // Just go back to running the restore queue
8527                    keyValueAgentCleanup();
8528
8529                    // If there was widget state associated with this app, get the OS to
8530                    // incorporate it into current bookeeping and then pass that along to
8531                    // the app as part of the restore-time work.
8532                    if (mWidgetData != null) {
8533                        restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
8534                    }
8535
8536                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
8537                    break;
8538                }
8539
8540                default: {
8541                    // Some kind of horrible semantic error; we're in an unexpected state.
8542                    // Back off hard and wind up.
8543                    Slog.e(TAG, "Unexpected restore callback into state " + mState);
8544                    keyValueAgentErrorCleanup();
8545                    nextState = UnifiedRestoreState.FINAL;
8546                    break;
8547                }
8548            }
8549
8550            executeNextState(nextState);
8551        }
8552
8553        // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
8554        @Override
8555        public void handleTimeout() {
8556            Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
8557            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8558                    mCurrentPackage.packageName, "restore timeout");
8559            // Handle like an agent that threw on invocation: wipe it and go on to the next
8560            keyValueAgentErrorCleanup();
8561            executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8562        }
8563
8564        void executeNextState(UnifiedRestoreState nextState) {
8565            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
8566                    + this + " nextState=" + nextState);
8567            mState = nextState;
8568            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
8569            mBackupHandler.sendMessage(msg);
8570        }
8571
8572        // restore observer support
8573        void sendStartRestore(int numPackages) {
8574            if (mObserver != null) {
8575                try {
8576                    mObserver.restoreStarting(numPackages);
8577                } catch (RemoteException e) {
8578                    Slog.w(TAG, "Restore observer went away: startRestore");
8579                    mObserver = null;
8580                }
8581            }
8582        }
8583
8584        void sendOnRestorePackage(String name) {
8585            if (mObserver != null) {
8586                if (mObserver != null) {
8587                    try {
8588                        mObserver.onUpdate(mCount, name);
8589                    } catch (RemoteException e) {
8590                        Slog.d(TAG, "Restore observer died in onUpdate");
8591                        mObserver = null;
8592                    }
8593                }
8594            }
8595        }
8596
8597        void sendEndRestore() {
8598            if (mObserver != null) {
8599                try {
8600                    mObserver.restoreFinished(mStatus);
8601                } catch (RemoteException e) {
8602                    Slog.w(TAG, "Restore observer went away: endRestore");
8603                    mObserver = null;
8604                }
8605            }
8606        }
8607    }
8608
8609    class PerformClearTask implements Runnable {
8610        IBackupTransport mTransport;
8611        PackageInfo mPackage;
8612
8613        PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
8614            mTransport = transport;
8615            mPackage = packageInfo;
8616        }
8617
8618        public void run() {
8619            try {
8620                // Clear the on-device backup state to ensure a full backup next time
8621                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
8622                File stateFile = new File(stateDir, mPackage.packageName);
8623                stateFile.delete();
8624
8625                // Tell the transport to remove all the persistent storage for the app
8626                // TODO - need to handle failures
8627                mTransport.clearBackupData(mPackage);
8628            } catch (RemoteException e) {
8629                // can't happen; the transport is local
8630            } catch (Exception e) {
8631                Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
8632            } finally {
8633                try {
8634                    // TODO - need to handle failures
8635                    mTransport.finishBackup();
8636                } catch (RemoteException e) {
8637                    // can't happen; the transport is local
8638                }
8639
8640                // Last but not least, release the cpu
8641                mWakelock.release();
8642            }
8643        }
8644    }
8645
8646    class PerformInitializeTask implements Runnable {
8647        HashSet<String> mQueue;
8648
8649        PerformInitializeTask(HashSet<String> transportNames) {
8650            mQueue = transportNames;
8651        }
8652
8653        public void run() {
8654            try {
8655                for (String transportName : mQueue) {
8656                    IBackupTransport transport = getTransport(transportName);
8657                    if (transport == null) {
8658                        Slog.e(TAG, "Requested init for " + transportName + " but not found");
8659                        continue;
8660                    }
8661
8662                    Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
8663                    EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
8664                    long startRealtime = SystemClock.elapsedRealtime();
8665                    int status = transport.initializeDevice();
8666
8667                    if (status == BackupTransport.TRANSPORT_OK) {
8668                        status = transport.finishBackup();
8669                    }
8670
8671                    // Okay, the wipe really happened.  Clean up our local bookkeeping.
8672                    if (status == BackupTransport.TRANSPORT_OK) {
8673                        Slog.i(TAG, "Device init successful");
8674                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
8675                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
8676                        resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
8677                        EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
8678                        synchronized (mQueueLock) {
8679                            recordInitPendingLocked(false, transportName);
8680                        }
8681                    } else {
8682                        // If this didn't work, requeue this one and try again
8683                        // after a suitable interval
8684                        Slog.e(TAG, "Transport error in initializeDevice()");
8685                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
8686                        synchronized (mQueueLock) {
8687                            recordInitPendingLocked(true, transportName);
8688                        }
8689                        // do this via another alarm to make sure of the wakelock states
8690                        long delay = transport.requestBackupTime();
8691                        Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay);
8692                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
8693                                System.currentTimeMillis() + delay, mRunInitIntent);
8694                    }
8695                }
8696            } catch (RemoteException e) {
8697                // can't happen; the transports are local
8698            } catch (Exception e) {
8699                Slog.e(TAG, "Unexpected error performing init", e);
8700            } finally {
8701                // Done; release the wakelock
8702                mWakelock.release();
8703            }
8704        }
8705    }
8706
8707    private void dataChangedImpl(String packageName) {
8708        HashSet<String> targets = dataChangedTargets(packageName);
8709        dataChangedImpl(packageName, targets);
8710    }
8711
8712    private void dataChangedImpl(String packageName, HashSet<String> targets) {
8713        // Record that we need a backup pass for the caller.  Since multiple callers
8714        // may share a uid, we need to note all candidates within that uid and schedule
8715        // a backup pass for each of them.
8716        if (targets == null) {
8717            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8718                   + " uid=" + Binder.getCallingUid());
8719            return;
8720        }
8721
8722        synchronized (mQueueLock) {
8723            // Note that this client has made data changes that need to be backed up
8724            if (targets.contains(packageName)) {
8725                // Add the caller to the set of pending backups.  If there is
8726                // one already there, then overwrite it, but no harm done.
8727                BackupRequest req = new BackupRequest(packageName);
8728                if (mPendingBackups.put(packageName, req) == null) {
8729                    if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
8730
8731                    // Journal this request in case of crash.  The put()
8732                    // operation returned null when this package was not already
8733                    // in the set; we want to avoid touching the disk redundantly.
8734                    writeToJournalLocked(packageName);
8735                }
8736            }
8737        }
8738
8739        // ...and schedule a backup pass if necessary
8740        KeyValueBackupJob.schedule(mContext);
8741    }
8742
8743    // Note: packageName is currently unused, but may be in the future
8744    private HashSet<String> dataChangedTargets(String packageName) {
8745        // If the caller does not hold the BACKUP permission, it can only request a
8746        // backup of its own data.
8747        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8748                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8749            synchronized (mBackupParticipants) {
8750                return mBackupParticipants.get(Binder.getCallingUid());
8751            }
8752        }
8753
8754        // a caller with full permission can ask to back up any participating app
8755        HashSet<String> targets = new HashSet<String>();
8756        if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
8757            targets.add(PACKAGE_MANAGER_SENTINEL);
8758        } else {
8759            synchronized (mBackupParticipants) {
8760                int N = mBackupParticipants.size();
8761                for (int i = 0; i < N; i++) {
8762                    HashSet<String> s = mBackupParticipants.valueAt(i);
8763                    if (s != null) {
8764                        targets.addAll(s);
8765                    }
8766                }
8767            }
8768        }
8769        return targets;
8770    }
8771
8772    private void writeToJournalLocked(String str) {
8773        RandomAccessFile out = null;
8774        try {
8775            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
8776            out = new RandomAccessFile(mJournal, "rws");
8777            out.seek(out.length());
8778            out.writeUTF(str);
8779        } catch (IOException e) {
8780            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
8781            mJournal = null;
8782        } finally {
8783            try { if (out != null) out.close(); } catch (IOException e) {}
8784        }
8785    }
8786
8787    // ----- IBackupManager binder interface -----
8788
8789    public void dataChanged(final String packageName) {
8790        final int callingUserHandle = UserHandle.getCallingUserId();
8791        if (callingUserHandle != UserHandle.USER_SYSTEM) {
8792            // TODO: http://b/22388012
8793            // App is running under a non-owner user profile.  For now, we do not back
8794            // up data from secondary user profiles.
8795            // TODO: backups for all user profiles although don't add backup for profiles
8796            // without adding admin control in DevicePolicyManager.
8797            if (MORE_DEBUG) {
8798                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
8799                        + callingUserHandle);
8800            }
8801            return;
8802        }
8803
8804        final HashSet<String> targets = dataChangedTargets(packageName);
8805        if (targets == null) {
8806            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8807                   + " uid=" + Binder.getCallingUid());
8808            return;
8809        }
8810
8811        mBackupHandler.post(new Runnable() {
8812                public void run() {
8813                    dataChangedImpl(packageName, targets);
8814                }
8815            });
8816    }
8817
8818    // Clear the given package's backup data from the current transport
8819    public void clearBackupData(String transportName, String packageName) {
8820        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
8821        PackageInfo info;
8822        try {
8823            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
8824        } catch (NameNotFoundException e) {
8825            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
8826            return;
8827        }
8828
8829        // If the caller does not hold the BACKUP permission, it can only request a
8830        // wipe of its own backed-up data.
8831        HashSet<String> apps;
8832        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8833                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8834            apps = mBackupParticipants.get(Binder.getCallingUid());
8835        } else {
8836            // a caller with full permission can ask to back up any participating app
8837            // !!! TODO: allow data-clear of ANY app?
8838            if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
8839            apps = new HashSet<String>();
8840            int N = mBackupParticipants.size();
8841            for (int i = 0; i < N; i++) {
8842                HashSet<String> s = mBackupParticipants.valueAt(i);
8843                if (s != null) {
8844                    apps.addAll(s);
8845                }
8846            }
8847        }
8848
8849        // Is the given app an available participant?
8850        if (apps.contains(packageName)) {
8851            // found it; fire off the clear request
8852            if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
8853            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
8854            synchronized (mQueueLock) {
8855                final IBackupTransport transport = getTransport(transportName);
8856                if (transport == null) {
8857                    // transport is currently unavailable -- make sure to retry
8858                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
8859                            new ClearRetryParams(transportName, packageName));
8860                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
8861                    return;
8862                }
8863                long oldId = Binder.clearCallingIdentity();
8864                mWakelock.acquire();
8865                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
8866                        new ClearParams(transport, info));
8867                mBackupHandler.sendMessage(msg);
8868                Binder.restoreCallingIdentity(oldId);
8869            }
8870        }
8871    }
8872
8873    // Run a backup pass immediately for any applications that have declared
8874    // that they have pending updates.
8875    public void backupNow() {
8876        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
8877
8878        if (mPowerManager.isPowerSaveMode()) {
8879            if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
8880            KeyValueBackupJob.schedule(mContext);   // try again in several hours
8881        } else {
8882            if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
8883            synchronized (mQueueLock) {
8884                // Fire the intent that kicks off the whole shebang...
8885                try {
8886                    mRunBackupIntent.send();
8887                } catch (PendingIntent.CanceledException e) {
8888                    // should never happen
8889                    Slog.e(TAG, "run-backup intent cancelled!");
8890                }
8891
8892                // ...and cancel any pending scheduled job, because we've just superseded it
8893                KeyValueBackupJob.cancel(mContext);
8894            }
8895        }
8896    }
8897
8898    boolean deviceIsProvisioned() {
8899        final ContentResolver resolver = mContext.getContentResolver();
8900        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
8901    }
8902
8903    // Run a *full* backup pass for the given packages, writing the resulting data stream
8904    // to the supplied file descriptor.  This method is synchronous and does not return
8905    // to the caller until the backup has been completed.
8906    //
8907    // This is the variant used by 'adb backup'; it requires on-screen confirmation
8908    // by the user because it can be used to offload data over untrusted USB.
8909    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
8910            boolean includeObbs, boolean includeShared, boolean doWidgets,
8911            boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) {
8912        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
8913
8914        final int callingUserHandle = UserHandle.getCallingUserId();
8915        // TODO: http://b/22388012
8916        if (callingUserHandle != UserHandle.USER_SYSTEM) {
8917            throw new IllegalStateException("Backup supported only for the device owner");
8918        }
8919
8920        // Validate
8921        if (!doAllApps) {
8922            if (!includeShared) {
8923                // If we're backing up shared data (sdcard or equivalent), then we can run
8924                // without any supplied app names.  Otherwise, we'd be doing no work, so
8925                // report the error.
8926                if (pkgList == null || pkgList.length == 0) {
8927                    throw new IllegalArgumentException(
8928                            "Backup requested but neither shared nor any apps named");
8929                }
8930            }
8931        }
8932
8933        long oldId = Binder.clearCallingIdentity();
8934        try {
8935            // Doesn't make sense to do a full backup prior to setup
8936            if (!deviceIsProvisioned()) {
8937                Slog.i(TAG, "Full backup not supported before setup");
8938                return;
8939            }
8940
8941            if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
8942                    + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
8943                    + " system=" + includeSystem + " pkgs=" + pkgList);
8944            Slog.i(TAG, "Beginning full backup...");
8945
8946            FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
8947                    includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList);
8948            final int token = generateToken();
8949            synchronized (mFullConfirmations) {
8950                mFullConfirmations.put(token, params);
8951            }
8952
8953            // start up the confirmation UI
8954            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
8955            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
8956                Slog.e(TAG, "Unable to launch full backup confirmation");
8957                mFullConfirmations.delete(token);
8958                return;
8959            }
8960
8961            // make sure the screen is lit for the user interaction
8962            mPowerManager.userActivity(SystemClock.uptimeMillis(),
8963                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
8964                    0);
8965
8966            // start the confirmation countdown
8967            startConfirmationTimeout(token, params);
8968
8969            // wait for the backup to be performed
8970            if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
8971            waitForCompletion(params);
8972        } finally {
8973            try {
8974                fd.close();
8975            } catch (IOException e) {
8976                // just eat it
8977            }
8978            Binder.restoreCallingIdentity(oldId);
8979            Slog.d(TAG, "Full backup processing complete.");
8980        }
8981    }
8982
8983    public void fullTransportBackup(String[] pkgNames) {
8984        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
8985                "fullTransportBackup");
8986
8987        final int callingUserHandle = UserHandle.getCallingUserId();
8988        // TODO: http://b/22388012
8989        if (callingUserHandle != UserHandle.USER_SYSTEM) {
8990            throw new IllegalStateException("Restore supported only for the device owner");
8991        }
8992
8993        if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
8994            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
8995        } else {
8996            if (DEBUG) {
8997                Slog.d(TAG, "fullTransportBackup()");
8998            }
8999
9000            CountDownLatch latch = new CountDownLatch(1);
9001            PerformFullTransportBackupTask task = new PerformFullTransportBackupTask(null, pkgNames,
9002                    false, null, latch, null, false /* userInitiated */);
9003            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
9004            mWakelock.acquire();
9005            (new Thread(task, "full-transport-master")).start();
9006            do {
9007                try {
9008                    latch.await();
9009                    break;
9010                } catch (InterruptedException e) {
9011                    // Just go back to waiting for the latch to indicate completion
9012                }
9013            } while (true);
9014
9015            // We just ran a backup on these packages, so kick them to the end of the queue
9016            final long now = System.currentTimeMillis();
9017            for (String pkg : pkgNames) {
9018                enqueueFullBackup(pkg, now);
9019            }
9020        }
9021
9022        if (DEBUG) {
9023            Slog.d(TAG, "Done with full transport backup.");
9024        }
9025    }
9026
9027    public void fullRestore(ParcelFileDescriptor fd) {
9028        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
9029
9030        final int callingUserHandle = UserHandle.getCallingUserId();
9031        // TODO: http://b/22388012
9032        if (callingUserHandle != UserHandle.USER_SYSTEM) {
9033            throw new IllegalStateException("Restore supported only for the device owner");
9034        }
9035
9036        long oldId = Binder.clearCallingIdentity();
9037
9038        try {
9039            // Check whether the device has been provisioned -- we don't handle
9040            // full restores prior to completing the setup process.
9041            if (!deviceIsProvisioned()) {
9042                Slog.i(TAG, "Full restore not permitted before setup");
9043                return;
9044            }
9045
9046            Slog.i(TAG, "Beginning full restore...");
9047
9048            FullRestoreParams params = new FullRestoreParams(fd);
9049            final int token = generateToken();
9050            synchronized (mFullConfirmations) {
9051                mFullConfirmations.put(token, params);
9052            }
9053
9054            // start up the confirmation UI
9055            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
9056            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
9057                Slog.e(TAG, "Unable to launch full restore confirmation");
9058                mFullConfirmations.delete(token);
9059                return;
9060            }
9061
9062            // make sure the screen is lit for the user interaction
9063            mPowerManager.userActivity(SystemClock.uptimeMillis(),
9064                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
9065                    0);
9066
9067            // start the confirmation countdown
9068            startConfirmationTimeout(token, params);
9069
9070            // wait for the restore to be performed
9071            if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
9072            waitForCompletion(params);
9073        } finally {
9074            try {
9075                fd.close();
9076            } catch (IOException e) {
9077                Slog.w(TAG, "Error trying to close fd after full restore: " + e);
9078            }
9079            Binder.restoreCallingIdentity(oldId);
9080            Slog.i(TAG, "Full restore processing complete.");
9081        }
9082    }
9083
9084    boolean startConfirmationUi(int token, String action) {
9085        try {
9086            Intent confIntent = new Intent(action);
9087            confIntent.setClassName("com.android.backupconfirm",
9088                    "com.android.backupconfirm.BackupRestoreConfirmation");
9089            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
9090            confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
9091            mContext.startActivity(confIntent);
9092        } catch (ActivityNotFoundException e) {
9093            return false;
9094        }
9095        return true;
9096    }
9097
9098    void startConfirmationTimeout(int token, FullParams params) {
9099        if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
9100                + TIMEOUT_FULL_CONFIRMATION + " millis");
9101        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
9102                token, 0, params);
9103        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
9104    }
9105
9106    void waitForCompletion(FullParams params) {
9107        synchronized (params.latch) {
9108            while (params.latch.get() == false) {
9109                try {
9110                    params.latch.wait();
9111                } catch (InterruptedException e) { /* never interrupted */ }
9112            }
9113        }
9114    }
9115
9116    void signalFullBackupRestoreCompletion(FullParams params) {
9117        synchronized (params.latch) {
9118            params.latch.set(true);
9119            params.latch.notifyAll();
9120        }
9121    }
9122
9123    // Confirm that the previously-requested full backup/restore operation can proceed.  This
9124    // is used to require a user-facing disclosure about the operation.
9125    public void acknowledgeFullBackupOrRestore(int token, boolean allow,
9126            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
9127        if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
9128                + " allow=" + allow);
9129
9130        // TODO: possibly require not just this signature-only permission, but even
9131        // require that the specific designated confirmation-UI app uid is the caller?
9132        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
9133
9134        long oldId = Binder.clearCallingIdentity();
9135        try {
9136
9137            FullParams params;
9138            synchronized (mFullConfirmations) {
9139                params = mFullConfirmations.get(token);
9140                if (params != null) {
9141                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
9142                    mFullConfirmations.delete(token);
9143
9144                    if (allow) {
9145                        final int verb = params instanceof FullBackupParams
9146                                ? MSG_RUN_ADB_BACKUP
9147                                : MSG_RUN_ADB_RESTORE;
9148
9149                        params.observer = observer;
9150                        params.curPassword = curPassword;
9151
9152                        params.encryptPassword = encPpassword;
9153
9154                        if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
9155                        mWakelock.acquire();
9156                        Message msg = mBackupHandler.obtainMessage(verb, params);
9157                        mBackupHandler.sendMessage(msg);
9158                    } else {
9159                        Slog.w(TAG, "User rejected full backup/restore operation");
9160                        // indicate completion without having actually transferred any data
9161                        signalFullBackupRestoreCompletion(params);
9162                    }
9163                } else {
9164                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
9165                }
9166            }
9167        } finally {
9168            Binder.restoreCallingIdentity(oldId);
9169        }
9170    }
9171
9172    // Enable/disable backups
9173    public void setBackupEnabled(boolean enable) {
9174        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9175                "setBackupEnabled");
9176
9177        Slog.i(TAG, "Backup enabled => " + enable);
9178
9179        long oldId = Binder.clearCallingIdentity();
9180        try {
9181            boolean wasEnabled = mEnabled;
9182            synchronized (this) {
9183                Settings.Secure.putInt(mContext.getContentResolver(),
9184                        Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
9185                mEnabled = enable;
9186            }
9187
9188            synchronized (mQueueLock) {
9189                if (enable && !wasEnabled && mProvisioned) {
9190                    // if we've just been enabled, start scheduling backup passes
9191                    KeyValueBackupJob.schedule(mContext);
9192                    scheduleNextFullBackupJob(0);
9193                } else if (!enable) {
9194                    // No longer enabled, so stop running backups
9195                    if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
9196
9197                    KeyValueBackupJob.cancel(mContext);
9198
9199                    // This also constitutes an opt-out, so we wipe any data for
9200                    // this device from the backend.  We start that process with
9201                    // an alarm in order to guarantee wakelock states.
9202                    if (wasEnabled && mProvisioned) {
9203                        // NOTE: we currently flush every registered transport, not just
9204                        // the currently-active one.
9205                        HashSet<String> allTransports;
9206                        synchronized (mTransports) {
9207                            allTransports = new HashSet<String>(mTransports.keySet());
9208                        }
9209                        // build the set of transports for which we are posting an init
9210                        for (String transport : allTransports) {
9211                            recordInitPendingLocked(true, transport);
9212                        }
9213                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
9214                                mRunInitIntent);
9215                    }
9216                }
9217            }
9218        } finally {
9219            Binder.restoreCallingIdentity(oldId);
9220        }
9221    }
9222
9223    // Enable/disable automatic restore of app data at install time
9224    public void setAutoRestore(boolean doAutoRestore) {
9225        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9226                "setAutoRestore");
9227
9228        Slog.i(TAG, "Auto restore => " + doAutoRestore);
9229
9230        final long oldId = Binder.clearCallingIdentity();
9231        try {
9232            synchronized (this) {
9233                Settings.Secure.putInt(mContext.getContentResolver(),
9234                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
9235                mAutoRestore = doAutoRestore;
9236            }
9237        } finally {
9238            Binder.restoreCallingIdentity(oldId);
9239        }
9240    }
9241
9242    // Mark the backup service as having been provisioned
9243    public void setBackupProvisioned(boolean available) {
9244        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9245                "setBackupProvisioned");
9246        /*
9247         * This is now a no-op; provisioning is simply the device's own setup state.
9248         */
9249    }
9250
9251    // Report whether the backup mechanism is currently enabled
9252    public boolean isBackupEnabled() {
9253        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
9254        return mEnabled;    // no need to synchronize just to read it
9255    }
9256
9257    // Report the name of the currently active transport
9258    public String getCurrentTransport() {
9259        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9260                "getCurrentTransport");
9261        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
9262        return mCurrentTransport;
9263    }
9264
9265    // Report all known, available backup transports
9266    public String[] listAllTransports() {
9267        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
9268
9269        String[] list = null;
9270        ArrayList<String> known = new ArrayList<String>();
9271        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
9272            if (entry.getValue() != null) {
9273                known.add(entry.getKey());
9274            }
9275        }
9276
9277        if (known.size() > 0) {
9278            list = new String[known.size()];
9279            known.toArray(list);
9280        }
9281        return list;
9282    }
9283
9284    // Select which transport to use for the next backup operation.
9285    public String selectBackupTransport(String transport) {
9286        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9287                "selectBackupTransport");
9288
9289        synchronized (mTransports) {
9290            final long oldId = Binder.clearCallingIdentity();
9291            try {
9292                String prevTransport = mCurrentTransport;
9293                mCurrentTransport = transport;
9294                Settings.Secure.putString(mContext.getContentResolver(),
9295                        Settings.Secure.BACKUP_TRANSPORT, transport);
9296                Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
9297                        + " returning " + prevTransport);
9298                return prevTransport;
9299            } finally {
9300                Binder.restoreCallingIdentity(oldId);
9301            }
9302        }
9303    }
9304
9305    // Supply the configuration Intent for the given transport.  If the name is not one
9306    // of the available transports, or if the transport does not supply any configuration
9307    // UI, the method returns null.
9308    public Intent getConfigurationIntent(String transportName) {
9309        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9310                "getConfigurationIntent");
9311
9312        synchronized (mTransports) {
9313            final IBackupTransport transport = mTransports.get(transportName);
9314            if (transport != null) {
9315                try {
9316                    final Intent intent = transport.configurationIntent();
9317                    if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
9318                            + intent);
9319                    return intent;
9320                } catch (RemoteException e) {
9321                    /* fall through to return null */
9322                }
9323            }
9324        }
9325
9326        return null;
9327    }
9328
9329    // Supply the configuration summary string for the given transport.  If the name is
9330    // not one of the available transports, or if the transport does not supply any
9331    // summary / destination string, the method can return null.
9332    //
9333    // This string is used VERBATIM as the summary text of the relevant Settings item!
9334    public String getDestinationString(String transportName) {
9335        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9336                "getDestinationString");
9337
9338        synchronized (mTransports) {
9339            final IBackupTransport transport = mTransports.get(transportName);
9340            if (transport != null) {
9341                try {
9342                    final String text = transport.currentDestinationString();
9343                    if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
9344                    return text;
9345                } catch (RemoteException e) {
9346                    /* fall through to return null */
9347                }
9348            }
9349        }
9350
9351        return null;
9352    }
9353
9354    // Supply the manage-data intent for the given transport.
9355    public Intent getDataManagementIntent(String transportName) {
9356        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9357                "getDataManagementIntent");
9358
9359        synchronized (mTransports) {
9360            final IBackupTransport transport = mTransports.get(transportName);
9361            if (transport != null) {
9362                try {
9363                    final Intent intent = transport.dataManagementIntent();
9364                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
9365                            + intent);
9366                    return intent;
9367                } catch (RemoteException e) {
9368                    /* fall through to return null */
9369                }
9370            }
9371        }
9372
9373        return null;
9374    }
9375
9376    // Supply the menu label for affordances that fire the manage-data intent
9377    // for the given transport.
9378    public String getDataManagementLabel(String transportName) {
9379        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9380                "getDataManagementLabel");
9381
9382        synchronized (mTransports) {
9383            final IBackupTransport transport = mTransports.get(transportName);
9384            if (transport != null) {
9385                try {
9386                    final String text = transport.dataManagementLabel();
9387                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
9388                    return text;
9389                } catch (RemoteException e) {
9390                    /* fall through to return null */
9391                }
9392            }
9393        }
9394
9395        return null;
9396    }
9397
9398    // Callback: a requested backup agent has been instantiated.  This should only
9399    // be called from the Activity Manager.
9400    public void agentConnected(String packageName, IBinder agentBinder) {
9401        synchronized(mAgentConnectLock) {
9402            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
9403                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
9404                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
9405                mConnectedAgent = agent;
9406                mConnecting = false;
9407            } else {
9408                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9409                        + " claiming agent connected");
9410            }
9411            mAgentConnectLock.notifyAll();
9412        }
9413    }
9414
9415    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
9416    // If the agent failed to come up in the first place, the agentBinder argument
9417    // will be null.  This should only be called from the Activity Manager.
9418    public void agentDisconnected(String packageName) {
9419        // TODO: handle backup being interrupted
9420        synchronized(mAgentConnectLock) {
9421            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
9422                mConnectedAgent = null;
9423                mConnecting = false;
9424            } else {
9425                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9426                        + " claiming agent disconnected");
9427            }
9428            mAgentConnectLock.notifyAll();
9429        }
9430    }
9431
9432    // An application being installed will need a restore pass, then the Package Manager
9433    // will need to be told when the restore is finished.
9434    public void restoreAtInstall(String packageName, int token) {
9435        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
9436            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9437                    + " attemping install-time restore");
9438            return;
9439        }
9440
9441        boolean skip = false;
9442
9443        long restoreSet = getAvailableRestoreToken(packageName);
9444        if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
9445                + " token=" + Integer.toHexString(token)
9446                + " restoreSet=" + Long.toHexString(restoreSet));
9447        if (restoreSet == 0) {
9448            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
9449            skip = true;
9450        }
9451
9452        // Do we have a transport to fetch data for us?
9453        IBackupTransport transport = getTransport(mCurrentTransport);
9454        if (transport == null) {
9455            if (DEBUG) Slog.w(TAG, "No transport");
9456            skip = true;
9457        }
9458
9459        if (!mAutoRestore) {
9460            if (DEBUG) {
9461                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
9462            }
9463            skip = true;
9464        }
9465
9466        if (!skip) {
9467            try {
9468                // okay, we're going to attempt a restore of this package from this restore set.
9469                // The eventual message back into the Package Manager to run the post-install
9470                // steps for 'token' will be issued from the restore handling code.
9471
9472                // This can throw and so *must* happen before the wakelock is acquired
9473                String dirName = transport.transportDirName();
9474
9475                mWakelock.acquire();
9476                if (MORE_DEBUG) {
9477                    Slog.d(TAG, "Restore at install of " + packageName);
9478                }
9479                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9480                msg.obj = new RestoreParams(transport, dirName, null,
9481                        restoreSet, packageName, token);
9482                mBackupHandler.sendMessage(msg);
9483            } catch (RemoteException e) {
9484                // Binding to the transport broke; back off and proceed with the installation.
9485                Slog.e(TAG, "Unable to contact transport");
9486                skip = true;
9487            }
9488        }
9489
9490        if (skip) {
9491            // Auto-restore disabled or no way to attempt a restore; just tell the Package
9492            // Manager to proceed with the post-install handling for this package.
9493            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
9494            try {
9495                mPackageManagerBinder.finishPackageInstall(token);
9496            } catch (RemoteException e) { /* can't happen */ }
9497        }
9498    }
9499
9500    // Hand off a restore session
9501    public IRestoreSession beginRestoreSession(String packageName, String transport) {
9502        if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
9503                + " transport=" + transport);
9504
9505        boolean needPermission = true;
9506        if (transport == null) {
9507            transport = mCurrentTransport;
9508
9509            if (packageName != null) {
9510                PackageInfo app = null;
9511                try {
9512                    app = mPackageManager.getPackageInfo(packageName, 0);
9513                } catch (NameNotFoundException nnf) {
9514                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9515                    throw new IllegalArgumentException("Package " + packageName + " not found");
9516                }
9517
9518                if (app.applicationInfo.uid == Binder.getCallingUid()) {
9519                    // So: using the current active transport, and the caller has asked
9520                    // that its own package will be restored.  In this narrow use case
9521                    // we do not require the caller to hold the permission.
9522                    needPermission = false;
9523                }
9524            }
9525        }
9526
9527        if (needPermission) {
9528            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9529                    "beginRestoreSession");
9530        } else {
9531            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
9532        }
9533
9534        synchronized(this) {
9535            if (mActiveRestoreSession != null) {
9536                Slog.d(TAG, "Restore session requested but one already active");
9537                return null;
9538            }
9539            mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
9540            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
9541        }
9542        return mActiveRestoreSession;
9543    }
9544
9545    void clearRestoreSession(ActiveRestoreSession currentSession) {
9546        synchronized(this) {
9547            if (currentSession != mActiveRestoreSession) {
9548                Slog.e(TAG, "ending non-current restore session");
9549            } else {
9550                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
9551                mActiveRestoreSession = null;
9552                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9553            }
9554        }
9555    }
9556
9557    // Note that a currently-active backup agent has notified us that it has
9558    // completed the given outstanding asynchronous backup/restore operation.
9559    public void opComplete(int token, long result) {
9560        if (MORE_DEBUG) {
9561            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
9562        }
9563        Operation op = null;
9564        synchronized (mCurrentOpLock) {
9565            op = mCurrentOperations.get(token);
9566            if (op != null) {
9567                op.state = OP_ACKNOWLEDGED;
9568            }
9569            mCurrentOpLock.notifyAll();
9570        }
9571
9572        // The completion callback, if any, is invoked on the handler
9573        if (op != null && op.callback != null) {
9574            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
9575            // NB: this cannot distinguish between results > 2 gig
9576            msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
9577            mBackupHandler.sendMessage(msg);
9578        }
9579    }
9580
9581    public boolean isAppEligibleForBackup(String packageName) {
9582        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9583                "isAppEligibleForBackup");
9584        try {
9585            PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
9586                    PackageManager.GET_SIGNATURES);
9587            if (!appIsEligibleForBackup(packageInfo.applicationInfo) ||
9588                    appIsStopped(packageInfo.applicationInfo)) {
9589                return false;
9590            }
9591            IBackupTransport transport = getTransport(mCurrentTransport);
9592            if (transport != null) {
9593                try {
9594                    return transport.isAppEligibleForBackup(packageInfo,
9595                        appGetsFullBackup(packageInfo));
9596                } catch (RemoteException e) {
9597                    Slog.e(TAG, "Unable to contact transport");
9598                }
9599            }
9600            // If transport is not present we couldn't tell that the package is not eligible.
9601            return true;
9602        } catch (NameNotFoundException e) {
9603            return false;
9604        }
9605    }
9606
9607    // ----- Restore session -----
9608
9609    class ActiveRestoreSession extends IRestoreSession.Stub {
9610        private static final String TAG = "RestoreSession";
9611
9612        private String mPackageName;
9613        private IBackupTransport mRestoreTransport = null;
9614        RestoreSet[] mRestoreSets = null;
9615        boolean mEnded = false;
9616        boolean mTimedOut = false;
9617
9618        ActiveRestoreSession(String packageName, String transport) {
9619            mPackageName = packageName;
9620            mRestoreTransport = getTransport(transport);
9621        }
9622
9623        public void markTimedOut() {
9624            mTimedOut = true;
9625        }
9626
9627        // --- Binder interface ---
9628        public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
9629            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9630                    "getAvailableRestoreSets");
9631            if (observer == null) {
9632                throw new IllegalArgumentException("Observer must not be null");
9633            }
9634
9635            if (mEnded) {
9636                throw new IllegalStateException("Restore session already ended");
9637            }
9638
9639            if (mTimedOut) {
9640                Slog.i(TAG, "Session already timed out");
9641                return -1;
9642            }
9643
9644            long oldId = Binder.clearCallingIdentity();
9645            try {
9646                if (mRestoreTransport == null) {
9647                    Slog.w(TAG, "Null transport getting restore sets");
9648                    return -1;
9649                }
9650
9651                // We know we're doing legit work now, so halt the timeout
9652                // until we're done.  It gets started again when the result
9653                // comes in.
9654                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9655
9656                // spin off the transport request to our service thread
9657                mWakelock.acquire();
9658                Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
9659                        new RestoreGetSetsParams(mRestoreTransport, this, observer));
9660                mBackupHandler.sendMessage(msg);
9661                return 0;
9662            } catch (Exception e) {
9663                Slog.e(TAG, "Error in getAvailableRestoreSets", e);
9664                return -1;
9665            } finally {
9666                Binder.restoreCallingIdentity(oldId);
9667            }
9668        }
9669
9670        public synchronized int restoreAll(long token, IRestoreObserver observer) {
9671            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9672                    "performRestore");
9673
9674            if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
9675                    + " observer=" + observer);
9676
9677            if (mEnded) {
9678                throw new IllegalStateException("Restore session already ended");
9679            }
9680
9681            if (mTimedOut) {
9682                Slog.i(TAG, "Session already timed out");
9683                return -1;
9684            }
9685
9686            if (mRestoreTransport == null || mRestoreSets == null) {
9687                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9688                return -1;
9689            }
9690
9691            if (mPackageName != null) {
9692                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9693                return -1;
9694            }
9695
9696            String dirName;
9697            try {
9698                dirName = mRestoreTransport.transportDirName();
9699            } catch (RemoteException e) {
9700                // Transport went AWOL; fail.
9701                Slog.e(TAG, "Unable to contact transport for restore");
9702                return -1;
9703            }
9704
9705            synchronized (mQueueLock) {
9706                for (int i = 0; i < mRestoreSets.length; i++) {
9707                    if (token == mRestoreSets[i].token) {
9708                        // Real work, so stop the session timeout until we finalize the restore
9709                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9710
9711                        long oldId = Binder.clearCallingIdentity();
9712                        mWakelock.acquire();
9713                        if (MORE_DEBUG) {
9714                            Slog.d(TAG, "restoreAll() kicking off");
9715                        }
9716                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9717                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
9718                                observer, token);
9719                        mBackupHandler.sendMessage(msg);
9720                        Binder.restoreCallingIdentity(oldId);
9721                        return 0;
9722                    }
9723                }
9724            }
9725
9726            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9727            return -1;
9728        }
9729
9730        // Restores of more than a single package are treated as 'system' restores
9731        public synchronized int restoreSome(long token, IRestoreObserver observer,
9732                String[] packages) {
9733            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9734                    "performRestore");
9735
9736            if (DEBUG) {
9737                StringBuilder b = new StringBuilder(128);
9738                b.append("restoreSome token=");
9739                b.append(Long.toHexString(token));
9740                b.append(" observer=");
9741                b.append(observer.toString());
9742                b.append(" packages=");
9743                if (packages == null) {
9744                    b.append("null");
9745                } else {
9746                    b.append('{');
9747                    boolean first = true;
9748                    for (String s : packages) {
9749                        if (!first) {
9750                            b.append(", ");
9751                        } else first = false;
9752                        b.append(s);
9753                    }
9754                    b.append('}');
9755                }
9756                Slog.d(TAG, b.toString());
9757            }
9758
9759            if (mEnded) {
9760                throw new IllegalStateException("Restore session already ended");
9761            }
9762
9763            if (mTimedOut) {
9764                Slog.i(TAG, "Session already timed out");
9765                return -1;
9766            }
9767
9768            if (mRestoreTransport == null || mRestoreSets == null) {
9769                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9770                return -1;
9771            }
9772
9773            if (mPackageName != null) {
9774                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9775                return -1;
9776            }
9777
9778            String dirName;
9779            try {
9780                dirName = mRestoreTransport.transportDirName();
9781            } catch (RemoteException e) {
9782                // Transport went AWOL; fail.
9783                Slog.e(TAG, "Unable to contact transport for restore");
9784                return -1;
9785            }
9786
9787            synchronized (mQueueLock) {
9788                for (int i = 0; i < mRestoreSets.length; i++) {
9789                    if (token == mRestoreSets[i].token) {
9790                        // Stop the session timeout until we finalize the restore
9791                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9792
9793                        long oldId = Binder.clearCallingIdentity();
9794                        mWakelock.acquire();
9795                        if (MORE_DEBUG) {
9796                            Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
9797                        }
9798                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9799                        msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
9800                                packages, packages.length > 1);
9801                        mBackupHandler.sendMessage(msg);
9802                        Binder.restoreCallingIdentity(oldId);
9803                        return 0;
9804                    }
9805                }
9806            }
9807
9808            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9809            return -1;
9810        }
9811
9812        public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
9813            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
9814
9815            if (mEnded) {
9816                throw new IllegalStateException("Restore session already ended");
9817            }
9818
9819            if (mTimedOut) {
9820                Slog.i(TAG, "Session already timed out");
9821                return -1;
9822            }
9823
9824            if (mPackageName != null) {
9825                if (! mPackageName.equals(packageName)) {
9826                    Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
9827                            + " on session for package " + mPackageName);
9828                    return -1;
9829                }
9830            }
9831
9832            PackageInfo app = null;
9833            try {
9834                app = mPackageManager.getPackageInfo(packageName, 0);
9835            } catch (NameNotFoundException nnf) {
9836                Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9837                return -1;
9838            }
9839
9840            // If the caller is not privileged and is not coming from the target
9841            // app's uid, throw a permission exception back to the caller.
9842            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
9843                    Binder.getCallingPid(), Binder.getCallingUid());
9844            if ((perm == PackageManager.PERMISSION_DENIED) &&
9845                    (app.applicationInfo.uid != Binder.getCallingUid())) {
9846                Slog.w(TAG, "restorePackage: bad packageName=" + packageName
9847                        + " or calling uid=" + Binder.getCallingUid());
9848                throw new SecurityException("No permission to restore other packages");
9849            }
9850
9851            // So far so good; we're allowed to try to restore this package.
9852            long oldId = Binder.clearCallingIdentity();
9853            try {
9854                // Check whether there is data for it in the current dataset, falling back
9855                // to the ancestral dataset if not.
9856                long token = getAvailableRestoreToken(packageName);
9857                if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName
9858                        + " token=" + Long.toHexString(token));
9859
9860                // If we didn't come up with a place to look -- no ancestral dataset and
9861                // the app has never been backed up from this device -- there's nothing
9862                // to do but return failure.
9863                if (token == 0) {
9864                    if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
9865                    return -1;
9866                }
9867
9868                String dirName;
9869                try {
9870                    dirName = mRestoreTransport.transportDirName();
9871                } catch (RemoteException e) {
9872                    // Transport went AWOL; fail.
9873                    Slog.e(TAG, "Unable to contact transport for restore");
9874                    return -1;
9875                }
9876
9877                // Stop the session timeout until we finalize the restore
9878                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9879
9880                // Ready to go:  enqueue the restore request and claim success
9881                mWakelock.acquire();
9882                if (MORE_DEBUG) {
9883                    Slog.d(TAG, "restorePackage() : " + packageName);
9884                }
9885                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9886                msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app);
9887                mBackupHandler.sendMessage(msg);
9888            } finally {
9889                Binder.restoreCallingIdentity(oldId);
9890            }
9891            return 0;
9892        }
9893
9894        // Posted to the handler to tear down a restore session in a cleanly synchronized way
9895        class EndRestoreRunnable implements Runnable {
9896            BackupManagerService mBackupManager;
9897            ActiveRestoreSession mSession;
9898
9899            EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
9900                mBackupManager = manager;
9901                mSession = session;
9902            }
9903
9904            public void run() {
9905                // clean up the session's bookkeeping
9906                synchronized (mSession) {
9907                    mSession.mRestoreTransport = null;
9908                    mSession.mEnded = true;
9909                }
9910
9911                // clean up the BackupManagerImpl side of the bookkeeping
9912                // and cancel any pending timeout message
9913                mBackupManager.clearRestoreSession(mSession);
9914            }
9915        }
9916
9917        public synchronized void endRestoreSession() {
9918            if (DEBUG) Slog.d(TAG, "endRestoreSession");
9919
9920            if (mTimedOut) {
9921                Slog.i(TAG, "Session already timed out");
9922                return;
9923            }
9924
9925            if (mEnded) {
9926                throw new IllegalStateException("Restore session already ended");
9927            }
9928
9929            mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
9930        }
9931    }
9932
9933    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
9934        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
9935
9936        long identityToken = Binder.clearCallingIdentity();
9937        try {
9938            if (args != null) {
9939                for (String arg : args) {
9940                    if ("-h".equals(arg)) {
9941                        pw.println("'dumpsys backup' optional arguments:");
9942                        pw.println("  -h       : this help text");
9943                        pw.println("  a[gents] : dump information about defined backup agents");
9944                        return;
9945                    } else if ("agents".startsWith(arg)) {
9946                        dumpAgents(pw);
9947                        return;
9948                    }
9949                }
9950            }
9951            dumpInternal(pw);
9952        } finally {
9953            Binder.restoreCallingIdentity(identityToken);
9954        }
9955    }
9956
9957    private void dumpAgents(PrintWriter pw) {
9958        List<PackageInfo> agentPackages = allAgentPackages();
9959        pw.println("Defined backup agents:");
9960        for (PackageInfo pkg : agentPackages) {
9961            pw.print("  ");
9962            pw.print(pkg.packageName); pw.println(':');
9963            pw.print("      "); pw.println(pkg.applicationInfo.backupAgentName);
9964        }
9965    }
9966
9967    private void dumpInternal(PrintWriter pw) {
9968        synchronized (mQueueLock) {
9969            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
9970                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
9971                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
9972            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
9973            if (mBackupRunning) pw.println("Backup currently running");
9974            pw.println("Last backup pass started: " + mLastBackupPass
9975                    + " (now = " + System.currentTimeMillis() + ')');
9976            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
9977
9978            pw.println("Available transports:");
9979            final String[] transports = listAllTransports();
9980            if (transports != null) {
9981                for (String t : listAllTransports()) {
9982                    pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
9983                    try {
9984                        IBackupTransport transport = getTransport(t);
9985                        File dir = new File(mBaseStateDir, transport.transportDirName());
9986                        pw.println("       destination: " + transport.currentDestinationString());
9987                        pw.println("       intent: " + transport.configurationIntent());
9988                        for (File f : dir.listFiles()) {
9989                            pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
9990                        }
9991                    } catch (Exception e) {
9992                        Slog.e(TAG, "Error in transport", e);
9993                        pw.println("        Error: " + e);
9994                    }
9995                }
9996            }
9997
9998            pw.println("Pending init: " + mPendingInits.size());
9999            for (String s : mPendingInits) {
10000                pw.println("    " + s);
10001            }
10002
10003            if (DEBUG_BACKUP_TRACE) {
10004                synchronized (mBackupTrace) {
10005                    if (!mBackupTrace.isEmpty()) {
10006                        pw.println("Most recent backup trace:");
10007                        for (String s : mBackupTrace) {
10008                            pw.println("   " + s);
10009                        }
10010                    }
10011                }
10012            }
10013
10014            pw.print("Ancestral: "); pw.println(Long.toHexString(mAncestralToken));
10015            pw.print("Current:   "); pw.println(Long.toHexString(mCurrentToken));
10016
10017            int N = mBackupParticipants.size();
10018            pw.println("Participants:");
10019            for (int i=0; i<N; i++) {
10020                int uid = mBackupParticipants.keyAt(i);
10021                pw.print("  uid: ");
10022                pw.println(uid);
10023                HashSet<String> participants = mBackupParticipants.valueAt(i);
10024                for (String app: participants) {
10025                    pw.println("    " + app);
10026                }
10027            }
10028
10029            pw.println("Ancestral packages: "
10030                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
10031            if (mAncestralPackages != null) {
10032                for (String pkg : mAncestralPackages) {
10033                    pw.println("    " + pkg);
10034                }
10035            }
10036
10037            pw.println("Ever backed up: " + mEverStoredApps.size());
10038            for (String pkg : mEverStoredApps) {
10039                pw.println("    " + pkg);
10040            }
10041
10042            pw.println("Pending key/value backup: " + mPendingBackups.size());
10043            for (BackupRequest req : mPendingBackups.values()) {
10044                pw.println("    " + req);
10045            }
10046
10047            pw.println("Full backup queue:" + mFullBackupQueue.size());
10048            for (FullBackupEntry entry : mFullBackupQueue) {
10049                pw.print("    "); pw.print(entry.lastBackup);
10050                pw.print(" : "); pw.println(entry.packageName);
10051            }
10052        }
10053    }
10054
10055    private static void sendBackupOnUpdate(IBackupObserver observer, String packageName,
10056            BackupProgress progress) {
10057        if (observer != null) {
10058            try {
10059                observer.onUpdate(packageName, progress);
10060            } catch (RemoteException e) {
10061                if (DEBUG) {
10062                    Slog.w(TAG, "Backup observer went away: onUpdate");
10063                }
10064            }
10065        }
10066    }
10067
10068    private static void sendBackupOnResult(IBackupObserver observer, String packageName,
10069            int status) {
10070        if (observer != null) {
10071            try {
10072                observer.onResult(packageName, status);
10073            } catch (RemoteException e) {
10074                if (DEBUG) {
10075                    Slog.w(TAG, "Backup observer went away: onResult");
10076                }
10077            }
10078        }
10079    }
10080
10081    private static void sendBackupFinished(IBackupObserver observer, int status) {
10082        if (observer != null) {
10083            try {
10084                observer.backupFinished(status);
10085            } catch (RemoteException e) {
10086                if (DEBUG) {
10087                    Slog.w(TAG, "Backup observer went away: backupFinished");
10088                }
10089            }
10090        }
10091    }
10092}
10093