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