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