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