BackupManagerService.java revision adfe8b86e9178a553b6db9722340fa4ff5201cf1
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.FullBackup;
30import android.app.backup.RestoreSet;
31import android.app.backup.IBackupManager;
32import android.app.backup.IFullBackupRestoreObserver;
33import android.app.backup.IRestoreObserver;
34import android.app.backup.IRestoreSession;
35import android.content.ActivityNotFoundException;
36import android.content.BroadcastReceiver;
37import android.content.ComponentName;
38import android.content.ContentResolver;
39import android.content.Context;
40import android.content.Intent;
41import android.content.IntentFilter;
42import android.content.ServiceConnection;
43import android.content.pm.ApplicationInfo;
44import android.content.pm.IPackageDataObserver;
45import android.content.pm.IPackageDeleteObserver;
46import android.content.pm.IPackageInstallObserver;
47import android.content.pm.IPackageManager;
48import android.content.pm.PackageInfo;
49import android.content.pm.PackageManager;
50import android.content.pm.ResolveInfo;
51import android.content.pm.ServiceInfo;
52import android.content.pm.Signature;
53import android.content.pm.PackageManager.NameNotFoundException;
54import android.database.ContentObserver;
55import android.net.Uri;
56import android.os.Binder;
57import android.os.Build;
58import android.os.Bundle;
59import android.os.Environment;
60import android.os.Handler;
61import android.os.HandlerThread;
62import android.os.IBinder;
63import android.os.Looper;
64import android.os.Message;
65import android.os.ParcelFileDescriptor;
66import android.os.PowerManager;
67import android.os.Process;
68import android.os.RemoteException;
69import android.os.SELinux;
70import android.os.ServiceManager;
71import android.os.SystemClock;
72import android.os.UserHandle;
73import android.os.WorkSource;
74import android.os.Environment.UserEnvironment;
75import android.os.storage.IMountService;
76import android.provider.Settings;
77import android.util.EventLog;
78import android.util.Log;
79import android.util.Slog;
80import android.util.SparseArray;
81import android.util.StringBuilderPrinter;
82
83import com.android.internal.backup.BackupConstants;
84import com.android.internal.backup.IBackupTransport;
85import com.android.internal.backup.IObbBackupService;
86import com.android.server.AppWidgetBackupBridge;
87import com.android.server.EventLogTags;
88import com.android.server.SystemService;
89import com.android.server.backup.PackageManagerBackupAgent.Metadata;
90
91import java.io.BufferedInputStream;
92import java.io.BufferedOutputStream;
93import java.io.ByteArrayInputStream;
94import java.io.ByteArrayOutputStream;
95import java.io.DataInputStream;
96import java.io.DataOutputStream;
97import java.io.EOFException;
98import java.io.File;
99import java.io.FileDescriptor;
100import java.io.FileInputStream;
101import java.io.FileNotFoundException;
102import java.io.FileOutputStream;
103import java.io.IOException;
104import java.io.InputStream;
105import java.io.OutputStream;
106import java.io.PrintWriter;
107import java.io.RandomAccessFile;
108import java.security.InvalidAlgorithmParameterException;
109import java.security.InvalidKeyException;
110import java.security.Key;
111import java.security.NoSuchAlgorithmException;
112import java.security.SecureRandom;
113import java.security.spec.InvalidKeySpecException;
114import java.security.spec.KeySpec;
115import java.text.SimpleDateFormat;
116import java.util.ArrayList;
117import java.util.Arrays;
118import java.util.Date;
119import java.util.HashMap;
120import java.util.HashSet;
121import java.util.Iterator;
122import java.util.List;
123import java.util.Map;
124import java.util.Map.Entry;
125import java.util.Random;
126import java.util.Set;
127import java.util.TreeMap;
128import java.util.concurrent.atomic.AtomicBoolean;
129import java.util.zip.Deflater;
130import java.util.zip.DeflaterOutputStream;
131import java.util.zip.InflaterInputStream;
132
133import javax.crypto.BadPaddingException;
134import javax.crypto.Cipher;
135import javax.crypto.CipherInputStream;
136import javax.crypto.CipherOutputStream;
137import javax.crypto.IllegalBlockSizeException;
138import javax.crypto.NoSuchPaddingException;
139import javax.crypto.SecretKey;
140import javax.crypto.SecretKeyFactory;
141import javax.crypto.spec.IvParameterSpec;
142import javax.crypto.spec.PBEKeySpec;
143import javax.crypto.spec.SecretKeySpec;
144
145import libcore.io.ErrnoException;
146import libcore.io.Libcore;
147
148public class BackupManagerService extends IBackupManager.Stub {
149
150    private static final String TAG = "BackupManagerService";
151    private static final boolean DEBUG = true;
152    private static final boolean MORE_DEBUG = false;
153
154    // System-private key used for backing up an app's widget state.  Must
155    // begin with U+FFxx by convention (we reserve all keys starting
156    // with U+FF00 or higher for system use).
157    static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
158
159    // Historical and current algorithm names
160    static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
161    static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
162
163    // Name and current contents version of the full-backup manifest file
164    //
165    // Manifest version history:
166    //
167    // 1 : initial release
168    static final String BACKUP_MANIFEST_FILENAME = "_manifest";
169    static final int BACKUP_MANIFEST_VERSION = 1;
170
171    // External archive format version history:
172    //
173    // 1 : initial release
174    // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
175    // 3 : introduced "_meta" metadata file; no other format change per se
176    static final int BACKUP_FILE_VERSION = 3;
177    static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
178    static final int BACKUP_PW_FILE_VERSION = 2;
179    static final String BACKUP_METADATA_FILENAME = "_meta";
180    static final int BACKUP_METADATA_VERSION = 1;
181    static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
182    static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
183
184    static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
185    static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
186
187    // How often we perform a backup pass.  Privileged external callers can
188    // trigger an immediate pass.
189    private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
190
191    // Random variation in backup scheduling time to avoid server load spikes
192    private static final int FUZZ_MILLIS = 5 * 60 * 1000;
193
194    // The amount of time between the initial provisioning of the device and
195    // the first backup pass.
196    private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
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_FULL_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_FULL_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
218    // backup task state machine tick
219    static final int MSG_BACKUP_RESTORE_STEP = 20;
220    static final int MSG_OP_COMPLETE = 21;
221
222    // Timeout interval for deciding that a bind or clear-data has taken too long
223    static final long TIMEOUT_INTERVAL = 10 * 1000;
224
225    // Timeout intervals for agent backup & restore operations
226    static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
227    static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
228    static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
229    static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
230
231    // User confirmation timeout for a full backup/restore operation.  It's this long in
232    // order to give them time to enter the backup password.
233    static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
234
235    private Context mContext;
236    private PackageManager mPackageManager;
237    IPackageManager mPackageManagerBinder;
238    private IActivityManager mActivityManager;
239    private PowerManager mPowerManager;
240    private AlarmManager mAlarmManager;
241    private IMountService mMountService;
242    IBackupManager mBackupManagerBinder;
243
244    boolean mEnabled;   // access to this is synchronized on 'this'
245    boolean mProvisioned;
246    boolean mAutoRestore;
247    PowerManager.WakeLock mWakelock;
248    HandlerThread mHandlerThread;
249    BackupHandler mBackupHandler;
250    PendingIntent mRunBackupIntent, mRunInitIntent;
251    BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
252    // map UIDs to the set of participating packages under that UID
253    final SparseArray<HashSet<String>> mBackupParticipants
254            = new SparseArray<HashSet<String>>();
255    // set of backup services that have pending changes
256    class BackupRequest {
257        public String packageName;
258
259        BackupRequest(String pkgName) {
260            packageName = pkgName;
261        }
262
263        public String toString() {
264            return "BackupRequest{pkg=" + packageName + "}";
265        }
266    }
267    // Backups that we haven't started yet.  Keys are package names.
268    HashMap<String,BackupRequest> mPendingBackups
269            = new HashMap<String,BackupRequest>();
270
271    // Pseudoname that we use for the Package Manager metadata "package"
272    static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
273
274    // locking around the pending-backup management
275    final Object mQueueLock = new Object();
276
277    // The thread performing the sequence of queued backups binds to each app's agent
278    // in succession.  Bind notifications are asynchronously delivered through the
279    // Activity Manager; use this lock object to signal when a requested binding has
280    // completed.
281    final Object mAgentConnectLock = new Object();
282    IBackupAgent mConnectedAgent;
283    volatile boolean mBackupRunning;
284    volatile boolean mConnecting;
285    volatile long mLastBackupPass;
286    volatile long mNextBackupPass;
287
288    // For debugging, we maintain a progress trace of operations during backup
289    static final boolean DEBUG_BACKUP_TRACE = true;
290    final List<String> mBackupTrace = new ArrayList<String>();
291
292    // A similar synchronization mechanism around clearing apps' data for restore
293    final Object mClearDataLock = new Object();
294    volatile boolean mClearingData;
295
296    // Transport bookkeeping
297    final HashMap<String,String> mTransportNames
298            = new HashMap<String,String>();             // component name -> registration name
299    final HashMap<String,IBackupTransport> mTransports
300            = new HashMap<String,IBackupTransport>();   // registration name -> binder
301    final ArrayList<TransportConnection> mTransportConnections
302            = new ArrayList<TransportConnection>();
303    String mCurrentTransport;
304    ActiveRestoreSession mActiveRestoreSession;
305
306    // Watch the device provisioning operation during setup
307    ContentObserver mProvisionedObserver;
308
309    public static final class Lifecycle extends SystemService {
310        private final BackupManagerService mService;
311
312        public Lifecycle(Context context) {
313            super(context);
314            mService = new BackupManagerService(context);
315        }
316
317        @Override
318        public void onStart() {
319            publishBinderService(Context.BACKUP_SERVICE, mService);
320        }
321    }
322
323    class ProvisionedObserver extends ContentObserver {
324        public ProvisionedObserver(Handler handler) {
325            super(handler);
326        }
327
328        public void onChange(boolean selfChange) {
329            final boolean wasProvisioned = mProvisioned;
330            final boolean isProvisioned = deviceIsProvisioned();
331            // latch: never unprovision
332            mProvisioned = wasProvisioned || isProvisioned;
333            if (MORE_DEBUG) {
334                Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
335                        + " is=" + isProvisioned + " now=" + mProvisioned);
336            }
337
338            synchronized (mQueueLock) {
339                if (mProvisioned && !wasProvisioned && mEnabled) {
340                    // we're now good to go, so start the backup alarms
341                    if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
342                    startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
343                }
344            }
345        }
346    }
347
348    class RestoreGetSetsParams {
349        public IBackupTransport transport;
350        public ActiveRestoreSession session;
351        public IRestoreObserver observer;
352
353        RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
354                IRestoreObserver _observer) {
355            transport = _transport;
356            session = _session;
357            observer = _observer;
358        }
359    }
360
361    class RestoreParams {
362        public IBackupTransport transport;
363        public String dirName;
364        public IRestoreObserver observer;
365        public long token;
366        public PackageInfo pkgInfo;
367        public int pmToken; // in post-install restore, the PM's token for this transaction
368        public boolean isSystemRestore;
369        public String[] filterSet;
370
371        // Restore a single package
372        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
373                long _token, PackageInfo _pkg, int _pmToken) {
374            transport = _transport;
375            dirName = _dirName;
376            observer = _obs;
377            token = _token;
378            pkgInfo = _pkg;
379            pmToken = _pmToken;
380            isSystemRestore = false;
381            filterSet = null;
382        }
383
384        // Restore everything possible.  This is the form that Setup Wizard or similar
385        // restore UXes use.
386        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
387                long _token) {
388            transport = _transport;
389            dirName = _dirName;
390            observer = _obs;
391            token = _token;
392            pkgInfo = null;
393            pmToken = 0;
394            isSystemRestore = true;
395            filterSet = null;
396        }
397
398        // Restore some set of packages.  Leave this one up to the caller to specify
399        // whether it's to be considered a system-level restore.
400        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
401                long _token, String[] _filterSet, boolean _isSystemRestore) {
402            transport = _transport;
403            dirName = _dirName;
404            observer = _obs;
405            token = _token;
406            pkgInfo = null;
407            pmToken = 0;
408            isSystemRestore = _isSystemRestore;
409            filterSet = _filterSet;
410        }
411    }
412
413    class ClearParams {
414        public IBackupTransport transport;
415        public PackageInfo packageInfo;
416
417        ClearParams(IBackupTransport _transport, PackageInfo _info) {
418            transport = _transport;
419            packageInfo = _info;
420        }
421    }
422
423    class ClearRetryParams {
424        public String transportName;
425        public String packageName;
426
427        ClearRetryParams(String transport, String pkg) {
428            transportName = transport;
429            packageName = pkg;
430        }
431    }
432
433    class FullParams {
434        public ParcelFileDescriptor fd;
435        public final AtomicBoolean latch;
436        public IFullBackupRestoreObserver observer;
437        public String curPassword;     // filled in by the confirmation step
438        public String encryptPassword;
439
440        FullParams() {
441            latch = new AtomicBoolean(false);
442        }
443    }
444
445    class FullBackupParams extends FullParams {
446        public boolean includeApks;
447        public boolean includeObbs;
448        public boolean includeShared;
449        public boolean doWidgets;
450        public boolean allApps;
451        public boolean includeSystem;
452        public String[] packages;
453
454        FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
455                boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem,
456                String[] pkgList) {
457            fd = output;
458            includeApks = saveApks;
459            includeObbs = saveObbs;
460            includeShared = saveShared;
461            doWidgets = alsoWidgets;
462            allApps = doAllApps;
463            includeSystem = doSystem;
464            packages = pkgList;
465        }
466    }
467
468    class FullRestoreParams extends FullParams {
469        FullRestoreParams(ParcelFileDescriptor input) {
470            fd = input;
471        }
472    }
473
474    // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
475    // token is the index of the entry in the pending-operations list.
476    static final int OP_PENDING = 0;
477    static final int OP_ACKNOWLEDGED = 1;
478    static final int OP_TIMEOUT = -1;
479
480    class Operation {
481        public int state;
482        public BackupRestoreTask callback;
483
484        Operation(int initialState, BackupRestoreTask callbackObj) {
485            state = initialState;
486            callback = callbackObj;
487        }
488    }
489    final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>();
490    final Object mCurrentOpLock = new Object();
491    final Random mTokenGenerator = new Random();
492
493    final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
494
495    // Where we keep our journal files and other bookkeeping
496    File mBaseStateDir;
497    File mDataDir;
498    File mJournalDir;
499    File mJournal;
500
501    // Backup password, if any, and the file where it's saved.  What is stored is not the
502    // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but
503    // persisted) salt.  Validation is performed by running the challenge text through the
504    // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches
505    // the saved hash string, then the challenge text matches the originally supplied
506    // password text.
507    private final SecureRandom mRng = new SecureRandom();
508    private String mPasswordHash;
509    private File mPasswordHashFile;
510    private int mPasswordVersion;
511    private File mPasswordVersionFile;
512    private byte[] mPasswordSalt;
513
514    // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys
515    static final int PBKDF2_HASH_ROUNDS = 10000;
516    static final int PBKDF2_KEY_SIZE = 256;     // bits
517    static final int PBKDF2_SALT_SIZE = 512;    // bits
518    static final String ENCRYPTION_ALGORITHM_NAME = "AES-256";
519
520    // Keep a log of all the apps we've ever backed up, and what the
521    // dataset tokens are for both the current backup dataset and
522    // the ancestral dataset.
523    private File mEverStored;
524    HashSet<String> mEverStoredApps = new HashSet<String>();
525
526    static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;  // increment when the schema changes
527    File mTokenFile;
528    Set<String> mAncestralPackages = null;
529    long mAncestralToken = 0;
530    long mCurrentToken = 0;
531
532    // Persistently track the need to do a full init
533    static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
534    HashSet<String> mPendingInits = new HashSet<String>();  // transport names
535
536    // Utility: build a new random integer token
537    int generateToken() {
538        int token;
539        do {
540            synchronized (mTokenGenerator) {
541                token = mTokenGenerator.nextInt();
542            }
543        } while (token < 0);
544        return token;
545    }
546
547    // ----- Asynchronous backup/restore handler thread -----
548
549    private class BackupHandler extends Handler {
550        public BackupHandler(Looper looper) {
551            super(looper);
552        }
553
554        public void handleMessage(Message msg) {
555
556            switch (msg.what) {
557            case MSG_RUN_BACKUP:
558            {
559                mLastBackupPass = System.currentTimeMillis();
560                mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
561
562                IBackupTransport transport = getTransport(mCurrentTransport);
563                if (transport == null) {
564                    Slog.v(TAG, "Backup requested but no transport available");
565                    synchronized (mQueueLock) {
566                        mBackupRunning = false;
567                    }
568                    mWakelock.release();
569                    break;
570                }
571
572                // snapshot the pending-backup set and work on that
573                ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
574                File oldJournal = mJournal;
575                synchronized (mQueueLock) {
576                    // Do we have any work to do?  Construct the work queue
577                    // then release the synchronization lock to actually run
578                    // the backup.
579                    if (mPendingBackups.size() > 0) {
580                        for (BackupRequest b: mPendingBackups.values()) {
581                            queue.add(b);
582                        }
583                        if (DEBUG) Slog.v(TAG, "clearing pending backups");
584                        mPendingBackups.clear();
585
586                        // Start a new backup-queue journal file too
587                        mJournal = null;
588
589                    }
590                }
591
592                // At this point, we have started a new journal file, and the old
593                // file identity is being passed to the backup processing task.
594                // When it completes successfully, that old journal file will be
595                // deleted.  If we crash prior to that, the old journal is parsed
596                // at next boot and the journaled requests fulfilled.
597                boolean staged = true;
598                if (queue.size() > 0) {
599                    // Spin up a backup state sequence and set it running
600                    try {
601                        String dirName = transport.transportDirName();
602                        PerformBackupTask pbt = new PerformBackupTask(transport, dirName,
603                                queue, oldJournal);
604                        Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
605                        sendMessage(pbtMessage);
606                    } catch (RemoteException e) {
607                        // unable to ask the transport its dir name -- transient failure, since
608                        // the above check succeeded.  Try again next time.
609                        Slog.e(TAG, "Transport became unavailable attempting backup");
610                        staged = false;
611                    }
612                } else {
613                    Slog.v(TAG, "Backup requested but nothing pending");
614                    staged = false;
615                }
616
617                if (!staged) {
618                    // if we didn't actually hand off the wakelock, rewind until next time
619                    synchronized (mQueueLock) {
620                        mBackupRunning = false;
621                    }
622                    mWakelock.release();
623                }
624                break;
625            }
626
627            case MSG_BACKUP_RESTORE_STEP:
628            {
629                try {
630                    BackupRestoreTask task = (BackupRestoreTask) msg.obj;
631                    if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing");
632                    task.execute();
633                } catch (ClassCastException e) {
634                    Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);
635                }
636                break;
637            }
638
639            case MSG_OP_COMPLETE:
640            {
641                try {
642                    BackupRestoreTask task = (BackupRestoreTask) msg.obj;
643                    task.operationComplete();
644                } catch (ClassCastException e) {
645                    Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
646                }
647                break;
648            }
649
650            case MSG_RUN_FULL_BACKUP:
651            {
652                // TODO: refactor full backup to be a looper-based state machine
653                // similar to normal backup/restore.
654                FullBackupParams params = (FullBackupParams)msg.obj;
655                PerformFullBackupTask task = new PerformFullBackupTask(params.fd,
656                        params.observer, params.includeApks, params.includeObbs,
657                        params.includeShared, params.doWidgets,
658                        params.curPassword, params.encryptPassword,
659                        params.allApps, params.includeSystem, params.packages, params.latch);
660                (new Thread(task)).start();
661                break;
662            }
663
664            case MSG_RUN_RESTORE:
665            {
666                RestoreParams params = (RestoreParams)msg.obj;
667                Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
668                PerformRestoreTask task = new PerformRestoreTask(
669                        params.transport, params.dirName, params.observer,
670                        params.token, params.pkgInfo, params.pmToken,
671                        params.isSystemRestore, params.filterSet);
672                Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
673                sendMessage(restoreMsg);
674                break;
675            }
676
677            case MSG_RUN_FULL_RESTORE:
678            {
679                // TODO: refactor full restore to be a looper-based state machine
680                // similar to normal backup/restore.
681                FullRestoreParams params = (FullRestoreParams)msg.obj;
682                PerformFullRestoreTask task = new PerformFullRestoreTask(params.fd,
683                        params.curPassword, params.encryptPassword,
684                        params.observer, params.latch);
685                (new Thread(task)).start();
686                break;
687            }
688
689            case MSG_RUN_CLEAR:
690            {
691                ClearParams params = (ClearParams)msg.obj;
692                (new PerformClearTask(params.transport, params.packageInfo)).run();
693                break;
694            }
695
696            case MSG_RETRY_CLEAR:
697            {
698                // reenqueues if the transport remains unavailable
699                ClearRetryParams params = (ClearRetryParams)msg.obj;
700                clearBackupData(params.transportName, params.packageName);
701                break;
702            }
703
704            case MSG_RUN_INITIALIZE:
705            {
706                HashSet<String> queue;
707
708                // Snapshot the pending-init queue and work on that
709                synchronized (mQueueLock) {
710                    queue = new HashSet<String>(mPendingInits);
711                    mPendingInits.clear();
712                }
713
714                (new PerformInitializeTask(queue)).run();
715                break;
716            }
717
718            case MSG_RETRY_INIT:
719            {
720                synchronized (mQueueLock) {
721                    recordInitPendingLocked(msg.arg1 != 0, (String)msg.obj);
722                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
723                            mRunInitIntent);
724                }
725                break;
726            }
727
728            case MSG_RUN_GET_RESTORE_SETS:
729            {
730                // Like other async operations, this is entered with the wakelock held
731                RestoreSet[] sets = null;
732                RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
733                try {
734                    sets = params.transport.getAvailableRestoreSets();
735                    // cache the result in the active session
736                    synchronized (params.session) {
737                        params.session.mRestoreSets = sets;
738                    }
739                    if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
740                } catch (Exception e) {
741                    Slog.e(TAG, "Error from transport getting set list");
742                } finally {
743                    if (params.observer != null) {
744                        try {
745                            params.observer.restoreSetsAvailable(sets);
746                        } catch (RemoteException re) {
747                            Slog.e(TAG, "Unable to report listing to observer");
748                        } catch (Exception e) {
749                            Slog.e(TAG, "Restore observer threw", e);
750                        }
751                    }
752
753                    // Done: reset the session timeout clock
754                    removeMessages(MSG_RESTORE_TIMEOUT);
755                    sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
756
757                    mWakelock.release();
758                }
759                break;
760            }
761
762            case MSG_TIMEOUT:
763            {
764                handleTimeout(msg.arg1, msg.obj);
765                break;
766            }
767
768            case MSG_RESTORE_TIMEOUT:
769            {
770                synchronized (BackupManagerService.this) {
771                    if (mActiveRestoreSession != null) {
772                        // Client app left the restore session dangling.  We know that it
773                        // can't be in the middle of an actual restore operation because
774                        // the timeout is suspended while a restore is in progress.  Clean
775                        // up now.
776                        Slog.w(TAG, "Restore session timed out; aborting");
777                        post(mActiveRestoreSession.new EndRestoreRunnable(
778                                BackupManagerService.this, mActiveRestoreSession));
779                    }
780                }
781            }
782
783            case MSG_FULL_CONFIRMATION_TIMEOUT:
784            {
785                synchronized (mFullConfirmations) {
786                    FullParams params = mFullConfirmations.get(msg.arg1);
787                    if (params != null) {
788                        Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
789
790                        // Release the waiter; timeout == completion
791                        signalFullBackupRestoreCompletion(params);
792
793                        // Remove the token from the set
794                        mFullConfirmations.delete(msg.arg1);
795
796                        // Report a timeout to the observer, if any
797                        if (params.observer != null) {
798                            try {
799                                params.observer.onTimeout();
800                            } catch (RemoteException e) {
801                                /* don't care if the app has gone away */
802                            }
803                        }
804                    } else {
805                        Slog.d(TAG, "couldn't find params for token " + msg.arg1);
806                    }
807                }
808                break;
809            }
810
811            case MSG_WIDGET_BROADCAST:
812            {
813                final Intent intent = (Intent) msg.obj;
814                mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
815                break;
816            }
817            }
818        }
819    }
820
821    // ----- Debug-only backup operation trace -----
822    void addBackupTrace(String s) {
823        if (DEBUG_BACKUP_TRACE) {
824            synchronized (mBackupTrace) {
825                mBackupTrace.add(s);
826            }
827        }
828    }
829
830    void clearBackupTrace() {
831        if (DEBUG_BACKUP_TRACE) {
832            synchronized (mBackupTrace) {
833                mBackupTrace.clear();
834            }
835        }
836    }
837
838    // ----- Main service implementation -----
839
840    public BackupManagerService(Context context) {
841        mContext = context;
842        mPackageManager = context.getPackageManager();
843        mPackageManagerBinder = AppGlobals.getPackageManager();
844        mActivityManager = ActivityManagerNative.getDefault();
845
846        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
847        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
848        mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
849
850        mBackupManagerBinder = asInterface(asBinder());
851
852        // spin up the backup/restore handler thread
853        mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
854        mHandlerThread.start();
855        mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
856
857        // Set up our bookkeeping
858        final ContentResolver resolver = context.getContentResolver();
859        boolean areEnabled = Settings.Secure.getInt(resolver,
860                Settings.Secure.BACKUP_ENABLED, 0) != 0;
861        mProvisioned = Settings.Global.getInt(resolver,
862                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
863        mAutoRestore = Settings.Secure.getInt(resolver,
864                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
865
866        mProvisionedObserver = new ProvisionedObserver(mBackupHandler);
867        resolver.registerContentObserver(
868                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
869                false, mProvisionedObserver);
870
871        // If Encrypted file systems is enabled or disabled, this call will return the
872        // correct directory.
873        mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
874        mBaseStateDir.mkdirs();
875        if (!SELinux.restorecon(mBaseStateDir)) {
876            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
877        }
878        mDataDir = Environment.getDownloadCacheDirectory();
879
880        mPasswordVersion = 1;       // unless we hear otherwise
881        mPasswordVersionFile = new File(mBaseStateDir, "pwversion");
882        if (mPasswordVersionFile.exists()) {
883            FileInputStream fin = null;
884            DataInputStream in = null;
885            try {
886                fin = new FileInputStream(mPasswordVersionFile);
887                in = new DataInputStream(fin);
888                mPasswordVersion = in.readInt();
889            } catch (IOException e) {
890                Slog.e(TAG, "Unable to read backup pw version");
891            } finally {
892                try {
893                    if (in != null) in.close();
894                    if (fin != null) fin.close();
895                } catch (IOException e) {
896                    Slog.w(TAG, "Error closing pw version files");
897                }
898            }
899        }
900
901        mPasswordHashFile = new File(mBaseStateDir, "pwhash");
902        if (mPasswordHashFile.exists()) {
903            FileInputStream fin = null;
904            DataInputStream in = null;
905            try {
906                fin = new FileInputStream(mPasswordHashFile);
907                in = new DataInputStream(new BufferedInputStream(fin));
908                // integer length of the salt array, followed by the salt,
909                // then the hex pw hash string
910                int saltLen = in.readInt();
911                byte[] salt = new byte[saltLen];
912                in.readFully(salt);
913                mPasswordHash = in.readUTF();
914                mPasswordSalt = salt;
915            } catch (IOException e) {
916                Slog.e(TAG, "Unable to read saved backup pw hash");
917            } finally {
918                try {
919                    if (in != null) in.close();
920                    if (fin != null) fin.close();
921                } catch (IOException e) {
922                    Slog.w(TAG, "Unable to close streams");
923                }
924            }
925        }
926
927        // Alarm receivers for scheduled backups & initialization operations
928        mRunBackupReceiver = new RunBackupReceiver();
929        IntentFilter filter = new IntentFilter();
930        filter.addAction(RUN_BACKUP_ACTION);
931        context.registerReceiver(mRunBackupReceiver, filter,
932                android.Manifest.permission.BACKUP, null);
933
934        mRunInitReceiver = new RunInitializeReceiver();
935        filter = new IntentFilter();
936        filter.addAction(RUN_INITIALIZE_ACTION);
937        context.registerReceiver(mRunInitReceiver, filter,
938                android.Manifest.permission.BACKUP, null);
939
940        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
941        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
942        mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
943
944        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
945        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
946        mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
947
948        // Set up the backup-request journaling
949        mJournalDir = new File(mBaseStateDir, "pending");
950        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
951        mJournal = null;        // will be created on first use
952
953        // Set up the various sorts of package tracking we do
954        initPackageTracking();
955
956        // Build our mapping of uid to backup client services.  This implicitly
957        // schedules a backup pass on the Package Manager metadata the first
958        // time anything needs to be backed up.
959        synchronized (mBackupParticipants) {
960            addPackageParticipantsLocked(null);
961        }
962
963        // Set up our transport options and initialize the default transport
964        // TODO: Don't create transports that we don't need to?
965        mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
966                Settings.Secure.BACKUP_TRANSPORT);
967        if ("".equals(mCurrentTransport)) {
968            mCurrentTransport = null;
969        }
970        if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
971
972        // Find transport hosts and bind to their services
973        Intent transportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
974        List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
975                transportServiceIntent, 0, UserHandle.USER_OWNER);
976        if (DEBUG) {
977            Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
978        }
979        if (hosts != null) {
980            if (MORE_DEBUG) {
981                for (int i = 0; i < hosts.size(); i++) {
982                    ServiceInfo info = hosts.get(i).serviceInfo;
983                    Slog.v(TAG, "   " + info.packageName + "/" + info.name);
984                }
985            }
986            for (int i = 0; i < hosts.size(); i++) {
987                try {
988                    ServiceInfo info = hosts.get(i).serviceInfo;
989                    PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
990                    if ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
991                        ComponentName svcName = new ComponentName(info.packageName, info.name);
992                        if (DEBUG) {
993                            Slog.i(TAG, "Binding to transport host " + svcName);
994                        }
995                        Intent intent = new Intent(transportServiceIntent);
996                        intent.setComponent(svcName);
997                        TransportConnection connection = new TransportConnection();
998                        mTransportConnections.add(connection);
999                        context.bindServiceAsUser(intent,
1000                                connection, Context.BIND_AUTO_CREATE,
1001                                UserHandle.OWNER);
1002                    } else {
1003                        Slog.w(TAG, "Transport package not privileged: " + info.packageName);
1004                    }
1005                } catch (Exception e) {
1006                    Slog.e(TAG, "Problem resolving transport service: " + e.getMessage());
1007                }
1008            }
1009        }
1010
1011        // Now that we know about valid backup participants, parse any
1012        // leftover journal files into the pending backup set
1013        parseLeftoverJournals();
1014
1015        // Power management
1016        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
1017
1018        // Start the backup passes going
1019        setBackupEnabled(areEnabled);
1020    }
1021
1022    private class RunBackupReceiver extends BroadcastReceiver {
1023        public void onReceive(Context context, Intent intent) {
1024            if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
1025                synchronized (mQueueLock) {
1026                    if (mPendingInits.size() > 0) {
1027                        // If there are pending init operations, we process those
1028                        // and then settle into the usual periodic backup schedule.
1029                        if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
1030                        try {
1031                            mAlarmManager.cancel(mRunInitIntent);
1032                            mRunInitIntent.send();
1033                        } catch (PendingIntent.CanceledException ce) {
1034                            Slog.e(TAG, "Run init intent cancelled");
1035                            // can't really do more than bail here
1036                        }
1037                    } else {
1038                        // Don't run backups now if we're disabled or not yet
1039                        // fully set up.
1040                        if (mEnabled && mProvisioned) {
1041                            if (!mBackupRunning) {
1042                                if (DEBUG) Slog.v(TAG, "Running a backup pass");
1043
1044                                // Acquire the wakelock and pass it to the backup thread.  it will
1045                                // be released once backup concludes.
1046                                mBackupRunning = true;
1047                                mWakelock.acquire();
1048
1049                                Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
1050                                mBackupHandler.sendMessage(msg);
1051                            } else {
1052                                Slog.i(TAG, "Backup time but one already running");
1053                            }
1054                        } else {
1055                            Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
1056                        }
1057                    }
1058                }
1059            }
1060        }
1061    }
1062
1063    private class RunInitializeReceiver extends BroadcastReceiver {
1064        public void onReceive(Context context, Intent intent) {
1065            if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
1066                synchronized (mQueueLock) {
1067                    if (DEBUG) Slog.v(TAG, "Running a device init");
1068
1069                    // Acquire the wakelock and pass it to the init thread.  it will
1070                    // be released once init concludes.
1071                    mWakelock.acquire();
1072
1073                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
1074                    mBackupHandler.sendMessage(msg);
1075                }
1076            }
1077        }
1078    }
1079
1080    private void initPackageTracking() {
1081        if (DEBUG) Slog.v(TAG, "Initializing package tracking");
1082
1083        // Remember our ancestral dataset
1084        mTokenFile = new File(mBaseStateDir, "ancestral");
1085        try {
1086            RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
1087            int version = tf.readInt();
1088            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
1089                mAncestralToken = tf.readLong();
1090                mCurrentToken = tf.readLong();
1091
1092                int numPackages = tf.readInt();
1093                if (numPackages >= 0) {
1094                    mAncestralPackages = new HashSet<String>();
1095                    for (int i = 0; i < numPackages; i++) {
1096                        String pkgName = tf.readUTF();
1097                        mAncestralPackages.add(pkgName);
1098                    }
1099                }
1100            }
1101            tf.close();
1102        } catch (FileNotFoundException fnf) {
1103            // Probably innocuous
1104            Slog.v(TAG, "No ancestral data");
1105        } catch (IOException e) {
1106            Slog.w(TAG, "Unable to read token file", e);
1107        }
1108
1109        // Keep a log of what apps we've ever backed up.  Because we might have
1110        // rebooted in the middle of an operation that was removing something from
1111        // this log, we sanity-check its contents here and reconstruct it.
1112        mEverStored = new File(mBaseStateDir, "processed");
1113        File tempProcessedFile = new File(mBaseStateDir, "processed.new");
1114
1115        // If we were in the middle of removing something from the ever-backed-up
1116        // file, there might be a transient "processed.new" file still present.
1117        // Ignore it -- we'll validate "processed" against the current package set.
1118        if (tempProcessedFile.exists()) {
1119            tempProcessedFile.delete();
1120        }
1121
1122        // If there are previous contents, parse them out then start a new
1123        // file to continue the recordkeeping.
1124        if (mEverStored.exists()) {
1125            RandomAccessFile temp = null;
1126            RandomAccessFile in = null;
1127
1128            try {
1129                temp = new RandomAccessFile(tempProcessedFile, "rws");
1130                in = new RandomAccessFile(mEverStored, "r");
1131
1132                while (true) {
1133                    PackageInfo info;
1134                    String pkg = in.readUTF();
1135                    try {
1136                        info = mPackageManager.getPackageInfo(pkg, 0);
1137                        mEverStoredApps.add(pkg);
1138                        temp.writeUTF(pkg);
1139                        if (MORE_DEBUG) Slog.v(TAG, "   + " + pkg);
1140                    } catch (NameNotFoundException e) {
1141                        // nope, this package was uninstalled; don't include it
1142                        if (MORE_DEBUG) Slog.v(TAG, "   - " + pkg);
1143                    }
1144                }
1145            } catch (EOFException e) {
1146                // Once we've rewritten the backup history log, atomically replace the
1147                // old one with the new one then reopen the file for continuing use.
1148                if (!tempProcessedFile.renameTo(mEverStored)) {
1149                    Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
1150                }
1151            } catch (IOException e) {
1152                Slog.e(TAG, "Error in processed file", e);
1153            } finally {
1154                try { if (temp != null) temp.close(); } catch (IOException e) {}
1155                try { if (in != null) in.close(); } catch (IOException e) {}
1156            }
1157        }
1158
1159        // Register for broadcasts about package install, etc., so we can
1160        // update the provider list.
1161        IntentFilter filter = new IntentFilter();
1162        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
1163        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1164        filter.addDataScheme("package");
1165        mContext.registerReceiver(mBroadcastReceiver, filter);
1166        // Register for events related to sdcard installation.
1167        IntentFilter sdFilter = new IntentFilter();
1168        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
1169        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1170        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
1171    }
1172
1173    private void parseLeftoverJournals() {
1174        for (File f : mJournalDir.listFiles()) {
1175            if (mJournal == null || f.compareTo(mJournal) != 0) {
1176                // This isn't the current journal, so it must be a leftover.  Read
1177                // out the package names mentioned there and schedule them for
1178                // backup.
1179                RandomAccessFile in = null;
1180                try {
1181                    Slog.i(TAG, "Found stale backup journal, scheduling");
1182                    in = new RandomAccessFile(f, "r");
1183                    while (true) {
1184                        String packageName = in.readUTF();
1185                        Slog.i(TAG, "  " + packageName);
1186                        dataChangedImpl(packageName);
1187                    }
1188                } catch (EOFException e) {
1189                    // no more data; we're done
1190                } catch (Exception e) {
1191                    Slog.e(TAG, "Can't read " + f, e);
1192                } finally {
1193                    // close/delete the file
1194                    try { if (in != null) in.close(); } catch (IOException e) {}
1195                    f.delete();
1196                }
1197            }
1198        }
1199    }
1200
1201    private SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) {
1202        return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds);
1203    }
1204
1205    private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) {
1206        try {
1207            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
1208            KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
1209            return keyFactory.generateSecret(ks);
1210        } catch (InvalidKeySpecException e) {
1211            Slog.e(TAG, "Invalid key spec for PBKDF2!");
1212        } catch (NoSuchAlgorithmException e) {
1213            Slog.e(TAG, "PBKDF2 unavailable!");
1214        }
1215        return null;
1216    }
1217
1218    private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) {
1219        SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds);
1220        if (key != null) {
1221            return byteArrayToHex(key.getEncoded());
1222        }
1223        return null;
1224    }
1225
1226    private String byteArrayToHex(byte[] data) {
1227        StringBuilder buf = new StringBuilder(data.length * 2);
1228        for (int i = 0; i < data.length; i++) {
1229            buf.append(Byte.toHexString(data[i], true));
1230        }
1231        return buf.toString();
1232    }
1233
1234    private byte[] hexToByteArray(String digits) {
1235        final int bytes = digits.length() / 2;
1236        if (2*bytes != digits.length()) {
1237            throw new IllegalArgumentException("Hex string must have an even number of digits");
1238        }
1239
1240        byte[] result = new byte[bytes];
1241        for (int i = 0; i < digits.length(); i += 2) {
1242            result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16);
1243        }
1244        return result;
1245    }
1246
1247    private byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) {
1248        char[] mkAsChar = new char[pwBytes.length];
1249        for (int i = 0; i < pwBytes.length; i++) {
1250            mkAsChar[i] = (char) pwBytes[i];
1251        }
1252
1253        Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds);
1254        return checksum.getEncoded();
1255    }
1256
1257    // Used for generating random salts or passwords
1258    private byte[] randomBytes(int bits) {
1259        byte[] array = new byte[bits / 8];
1260        mRng.nextBytes(array);
1261        return array;
1262    }
1263
1264    // Backup password management
1265    boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) {
1266        // First, on an encrypted device we require matching the device pw
1267        final boolean isEncrypted;
1268        try {
1269            isEncrypted = (mMountService.getEncryptionState() !=
1270                    IMountService.ENCRYPTION_STATE_NONE);
1271            if (isEncrypted) {
1272                if (DEBUG) {
1273                    Slog.i(TAG, "Device encrypted; verifying against device data pw");
1274                }
1275                // 0 means the password validated
1276                // -2 means device not encrypted
1277                // Any other result is either password failure or an error condition,
1278                // so we refuse the match
1279                final int result = mMountService.verifyEncryptionPassword(candidatePw);
1280                if (result == 0) {
1281                    if (MORE_DEBUG) Slog.d(TAG, "Pw verifies");
1282                    return true;
1283                } else if (result != -2) {
1284                    if (MORE_DEBUG) Slog.d(TAG, "Pw mismatch");
1285                    return false;
1286                } else {
1287                    // ...else the device is supposedly not encrypted.  HOWEVER, the
1288                    // query about the encryption state said that the device *is*
1289                    // encrypted, so ... we may have a problem.  Log it and refuse
1290                    // the backup.
1291                    Slog.e(TAG, "verified encryption state mismatch against query; no match allowed");
1292                    return false;
1293                }
1294            }
1295        } catch (Exception e) {
1296            // Something went wrong talking to the mount service.  This is very bad;
1297            // assume that we fail password validation.
1298            return false;
1299        }
1300
1301        if (mPasswordHash == null) {
1302            // no current password case -- require that 'currentPw' be null or empty
1303            if (candidatePw == null || "".equals(candidatePw)) {
1304                return true;
1305            } // else the non-empty candidate does not match the empty stored pw
1306        } else {
1307            // hash the stated current pw and compare to the stored one
1308            if (candidatePw != null && candidatePw.length() > 0) {
1309                String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds);
1310                if (mPasswordHash.equalsIgnoreCase(currentPwHash)) {
1311                    // candidate hash matches the stored hash -- the password matches
1312                    return true;
1313                }
1314            } // else the stored pw is nonempty but the candidate is empty; no match
1315        }
1316        return false;
1317    }
1318
1319    @Override
1320    public boolean setBackupPassword(String currentPw, String newPw) {
1321        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1322                "setBackupPassword");
1323
1324        // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes
1325        final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
1326
1327        // If the supplied pw doesn't hash to the the saved one, fail.  The password
1328        // might be caught in the legacy crypto mismatch; verify that too.
1329        if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
1330                && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
1331                        currentPw, PBKDF2_HASH_ROUNDS))) {
1332            return false;
1333        }
1334
1335        // Snap up to current on the pw file version
1336        mPasswordVersion = BACKUP_PW_FILE_VERSION;
1337        FileOutputStream pwFout = null;
1338        DataOutputStream pwOut = null;
1339        try {
1340            pwFout = new FileOutputStream(mPasswordVersionFile);
1341            pwOut = new DataOutputStream(pwFout);
1342            pwOut.writeInt(mPasswordVersion);
1343        } catch (IOException e) {
1344            Slog.e(TAG, "Unable to write backup pw version; password not changed");
1345            return false;
1346        } finally {
1347            try {
1348                if (pwOut != null) pwOut.close();
1349                if (pwFout != null) pwFout.close();
1350            } catch (IOException e) {
1351                Slog.w(TAG, "Unable to close pw version record");
1352            }
1353        }
1354
1355        // Clearing the password is okay
1356        if (newPw == null || newPw.isEmpty()) {
1357            if (mPasswordHashFile.exists()) {
1358                if (!mPasswordHashFile.delete()) {
1359                    // Unable to delete the old pw file, so fail
1360                    Slog.e(TAG, "Unable to clear backup password");
1361                    return false;
1362                }
1363            }
1364            mPasswordHash = null;
1365            mPasswordSalt = null;
1366            return true;
1367        }
1368
1369        try {
1370            // Okay, build the hash of the new backup password
1371            byte[] salt = randomBytes(PBKDF2_SALT_SIZE);
1372            String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS);
1373
1374            OutputStream pwf = null, buffer = null;
1375            DataOutputStream out = null;
1376            try {
1377                pwf = new FileOutputStream(mPasswordHashFile);
1378                buffer = new BufferedOutputStream(pwf);
1379                out = new DataOutputStream(buffer);
1380                // integer length of the salt array, followed by the salt,
1381                // then the hex pw hash string
1382                out.writeInt(salt.length);
1383                out.write(salt);
1384                out.writeUTF(newPwHash);
1385                out.flush();
1386                mPasswordHash = newPwHash;
1387                mPasswordSalt = salt;
1388                return true;
1389            } finally {
1390                if (out != null) out.close();
1391                if (buffer != null) buffer.close();
1392                if (pwf != null) pwf.close();
1393            }
1394        } catch (IOException e) {
1395            Slog.e(TAG, "Unable to set backup password");
1396        }
1397        return false;
1398    }
1399
1400    @Override
1401    public boolean hasBackupPassword() {
1402        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1403                "hasBackupPassword");
1404
1405        try {
1406            return (mMountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE)
1407                || (mPasswordHash != null && mPasswordHash.length() > 0);
1408        } catch (Exception e) {
1409            // If we can't talk to the mount service we have a serious problem; fail
1410            // "secure" i.e. assuming that we require a password
1411            return true;
1412        }
1413    }
1414
1415    private boolean backupPasswordMatches(String currentPw) {
1416        if (hasBackupPassword()) {
1417            final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
1418            if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
1419                    && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
1420                            currentPw, PBKDF2_HASH_ROUNDS))) {
1421                if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
1422                return false;
1423            }
1424        }
1425        return true;
1426    }
1427
1428    // Maintain persistent state around whether need to do an initialize operation.
1429    // Must be called with the queue lock held.
1430    void recordInitPendingLocked(boolean isPending, String transportName) {
1431        if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
1432                + " on transport " + transportName);
1433        mBackupHandler.removeMessages(MSG_RETRY_INIT);
1434
1435        try {
1436            IBackupTransport transport = getTransport(transportName);
1437            if (transport != null) {
1438                String transportDirName = transport.transportDirName();
1439                File stateDir = new File(mBaseStateDir, transportDirName);
1440                File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1441
1442                if (isPending) {
1443                    // We need an init before we can proceed with sending backup data.
1444                    // Record that with an entry in our set of pending inits, as well as
1445                    // journaling it via creation of a sentinel file.
1446                    mPendingInits.add(transportName);
1447                    try {
1448                        (new FileOutputStream(initPendingFile)).close();
1449                    } catch (IOException ioe) {
1450                        // Something is badly wrong with our permissions; just try to move on
1451                    }
1452                } else {
1453                    // No more initialization needed; wipe the journal and reset our state.
1454                    initPendingFile.delete();
1455                    mPendingInits.remove(transportName);
1456                }
1457                return; // done; don't fall through to the error case
1458            }
1459        } catch (RemoteException e) {
1460            // transport threw when asked its name; fall through to the lookup-failed case
1461        }
1462
1463        // The named transport doesn't exist or threw.  This operation is
1464        // important, so we record the need for a an init and post a message
1465        // to retry the init later.
1466        if (isPending) {
1467            mPendingInits.add(transportName);
1468            mBackupHandler.sendMessageDelayed(
1469                    mBackupHandler.obtainMessage(MSG_RETRY_INIT,
1470                            (isPending ? 1 : 0),
1471                            0,
1472                            transportName),
1473                    TRANSPORT_RETRY_INTERVAL);
1474        }
1475    }
1476
1477    // Reset all of our bookkeeping, in response to having been told that
1478    // the backend data has been wiped [due to idle expiry, for example],
1479    // so we must re-upload all saved settings.
1480    void resetBackupState(File stateFileDir) {
1481        synchronized (mQueueLock) {
1482            // Wipe the "what we've ever backed up" tracking
1483            mEverStoredApps.clear();
1484            mEverStored.delete();
1485
1486            mCurrentToken = 0;
1487            writeRestoreTokens();
1488
1489            // Remove all the state files
1490            for (File sf : stateFileDir.listFiles()) {
1491                // ... but don't touch the needs-init sentinel
1492                if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
1493                    sf.delete();
1494                }
1495            }
1496        }
1497
1498        // Enqueue a new backup of every participant
1499        synchronized (mBackupParticipants) {
1500            final int N = mBackupParticipants.size();
1501            for (int i=0; i<N; i++) {
1502                HashSet<String> participants = mBackupParticipants.valueAt(i);
1503                if (participants != null) {
1504                    for (String packageName : participants) {
1505                        dataChangedImpl(packageName);
1506                    }
1507                }
1508            }
1509        }
1510    }
1511
1512    // Add a transport to our set of available backends.  If 'transport' is null, this
1513    // is an unregistration, and the transport's entry is removed from our bookkeeping.
1514    private void registerTransport(String name, String component, IBackupTransport transport) {
1515        synchronized (mTransports) {
1516            if (DEBUG) Slog.v(TAG, "Registering transport "
1517                    + component + "::" + name + " = " + transport);
1518            if (transport != null) {
1519                mTransports.put(name, transport);
1520                mTransportNames.put(component, name);
1521            } else {
1522                mTransports.remove(mTransportNames.get(component));
1523                mTransportNames.remove(component);
1524                // Nothing further to do in the unregistration case
1525                return;
1526            }
1527        }
1528
1529        // If the init sentinel file exists, we need to be sure to perform the init
1530        // as soon as practical.  We also create the state directory at registration
1531        // time to ensure it's present from the outset.
1532        try {
1533            String transportName = transport.transportDirName();
1534            File stateDir = new File(mBaseStateDir, transportName);
1535            stateDir.mkdirs();
1536
1537            File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1538            if (initSentinel.exists()) {
1539                synchronized (mQueueLock) {
1540                    mPendingInits.add(transportName);
1541
1542                    // TODO: pick a better starting time than now + 1 minute
1543                    long delay = 1000 * 60; // one minute, in milliseconds
1544                    mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1545                            System.currentTimeMillis() + delay, mRunInitIntent);
1546                }
1547            }
1548        } catch (RemoteException e) {
1549            // the transport threw when asked its file naming prefs; declare it invalid
1550            Slog.e(TAG, "Unable to register transport as " + name);
1551            mTransportNames.remove(component);
1552            mTransports.remove(name);
1553        }
1554    }
1555
1556    // ----- Track installation/removal of packages -----
1557    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1558        public void onReceive(Context context, Intent intent) {
1559            if (DEBUG) Slog.d(TAG, "Received broadcast " + intent);
1560
1561            String action = intent.getAction();
1562            boolean replacing = false;
1563            boolean added = false;
1564            Bundle extras = intent.getExtras();
1565            String pkgList[] = null;
1566            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
1567                    Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
1568                Uri uri = intent.getData();
1569                if (uri == null) {
1570                    return;
1571                }
1572                String pkgName = uri.getSchemeSpecificPart();
1573                if (pkgName != null) {
1574                    pkgList = new String[] { pkgName };
1575                }
1576                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1577                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
1578            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
1579                added = true;
1580                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1581            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
1582                added = false;
1583                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1584            }
1585
1586            if (pkgList == null || pkgList.length == 0) {
1587                return;
1588            }
1589
1590            final int uid = extras.getInt(Intent.EXTRA_UID);
1591            if (added) {
1592                synchronized (mBackupParticipants) {
1593                    if (replacing) {
1594                        // This is the package-replaced case; we just remove the entry
1595                        // under the old uid and fall through to re-add.
1596                        removePackageParticipantsLocked(pkgList, uid);
1597                    }
1598                    addPackageParticipantsLocked(pkgList);
1599                }
1600            } else {
1601                if (replacing) {
1602                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
1603                } else {
1604                    synchronized (mBackupParticipants) {
1605                        removePackageParticipantsLocked(pkgList, uid);
1606                    }
1607                }
1608            }
1609        }
1610    };
1611
1612    // ----- Track connection to transports service -----
1613    class TransportConnection implements ServiceConnection {
1614        @Override
1615        public void onServiceConnected(ComponentName component, IBinder service) {
1616            if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
1617            try {
1618                IBackupTransport transport = IBackupTransport.Stub.asInterface(service);
1619                registerTransport(transport.name(), component.flattenToShortString(), transport);
1620            } catch (RemoteException e) {
1621                Slog.e(TAG, "Unable to register transport " + component);
1622            }
1623        }
1624
1625        @Override
1626        public void onServiceDisconnected(ComponentName component) {
1627            if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component);
1628            registerTransport(null, component.flattenToShortString(), null);
1629        }
1630    };
1631
1632    // Add the backup agents in the given packages to our set of known backup participants.
1633    // If 'packageNames' is null, adds all backup agents in the whole system.
1634    void addPackageParticipantsLocked(String[] packageNames) {
1635        // Look for apps that define the android:backupAgent attribute
1636        List<PackageInfo> targetApps = allAgentPackages();
1637        if (packageNames != null) {
1638            if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
1639            for (String packageName : packageNames) {
1640                addPackageParticipantsLockedInner(packageName, targetApps);
1641            }
1642        } else {
1643            if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
1644            addPackageParticipantsLockedInner(null, targetApps);
1645        }
1646    }
1647
1648    private void addPackageParticipantsLockedInner(String packageName,
1649            List<PackageInfo> targetPkgs) {
1650        if (MORE_DEBUG) {
1651            Slog.v(TAG, "Examining " + packageName + " for backup agent");
1652        }
1653
1654        for (PackageInfo pkg : targetPkgs) {
1655            if (packageName == null || pkg.packageName.equals(packageName)) {
1656                int uid = pkg.applicationInfo.uid;
1657                HashSet<String> set = mBackupParticipants.get(uid);
1658                if (set == null) {
1659                    set = new HashSet<String>();
1660                    mBackupParticipants.put(uid, set);
1661                }
1662                set.add(pkg.packageName);
1663                if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
1664
1665                // Schedule a backup for it on general principles
1666                if (DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
1667                dataChangedImpl(pkg.packageName);
1668            }
1669        }
1670    }
1671
1672    // Remove the given packages' entries from our known active set.
1673    void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
1674        if (packageNames == null) {
1675            Slog.w(TAG, "removePackageParticipants with null list");
1676            return;
1677        }
1678
1679        if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
1680                + " #" + packageNames.length);
1681        for (String pkg : packageNames) {
1682            // Known previous UID, so we know which package set to check
1683            HashSet<String> set = mBackupParticipants.get(oldUid);
1684            if (set != null && set.contains(pkg)) {
1685                removePackageFromSetLocked(set, pkg);
1686                if (set.isEmpty()) {
1687                    if (MORE_DEBUG) Slog.v(TAG, "  last one of this uid; purging set");
1688                    mBackupParticipants.remove(oldUid);
1689                }
1690            }
1691        }
1692    }
1693
1694    private void removePackageFromSetLocked(final HashSet<String> set,
1695            final String packageName) {
1696        if (set.contains(packageName)) {
1697            // Found it.  Remove this one package from the bookkeeping, and
1698            // if it's the last participating app under this uid we drop the
1699            // (now-empty) set as well.
1700            // Note that we deliberately leave it 'known' in the "ever backed up"
1701            // bookkeeping so that its current-dataset data will be retrieved
1702            // if the app is subsequently reinstalled
1703            if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
1704            set.remove(packageName);
1705            mPendingBackups.remove(packageName);
1706        }
1707    }
1708
1709    // Returns the set of all applications that define an android:backupAgent attribute
1710    List<PackageInfo> allAgentPackages() {
1711        // !!! TODO: cache this and regenerate only when necessary
1712        int flags = PackageManager.GET_SIGNATURES;
1713        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
1714        int N = packages.size();
1715        for (int a = N-1; a >= 0; a--) {
1716            PackageInfo pkg = packages.get(a);
1717            try {
1718                ApplicationInfo app = pkg.applicationInfo;
1719                if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
1720                        || app.backupAgentName == null) {
1721                    packages.remove(a);
1722                }
1723                else {
1724                    // we will need the shared library path, so look that up and store it here
1725                    app = mPackageManager.getApplicationInfo(pkg.packageName,
1726                            PackageManager.GET_SHARED_LIBRARY_FILES);
1727                    pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
1728                }
1729            } catch (NameNotFoundException e) {
1730                packages.remove(a);
1731            }
1732        }
1733        return packages;
1734    }
1735
1736    // Called from the backup task: record that the given app has been successfully
1737    // backed up at least once
1738    void logBackupComplete(String packageName) {
1739        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
1740
1741        synchronized (mEverStoredApps) {
1742            if (!mEverStoredApps.add(packageName)) return;
1743
1744            RandomAccessFile out = null;
1745            try {
1746                out = new RandomAccessFile(mEverStored, "rws");
1747                out.seek(out.length());
1748                out.writeUTF(packageName);
1749            } catch (IOException e) {
1750                Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
1751            } finally {
1752                try { if (out != null) out.close(); } catch (IOException e) {}
1753            }
1754        }
1755    }
1756
1757    // Remove our awareness of having ever backed up the given package
1758    void removeEverBackedUp(String packageName) {
1759        if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
1760        if (MORE_DEBUG) Slog.v(TAG, "New set:");
1761
1762        synchronized (mEverStoredApps) {
1763            // Rewrite the file and rename to overwrite.  If we reboot in the middle,
1764            // we'll recognize on initialization time that the package no longer
1765            // exists and fix it up then.
1766            File tempKnownFile = new File(mBaseStateDir, "processed.new");
1767            RandomAccessFile known = null;
1768            try {
1769                known = new RandomAccessFile(tempKnownFile, "rws");
1770                mEverStoredApps.remove(packageName);
1771                for (String s : mEverStoredApps) {
1772                    known.writeUTF(s);
1773                    if (MORE_DEBUG) Slog.v(TAG, "    " + s);
1774                }
1775                known.close();
1776                known = null;
1777                if (!tempKnownFile.renameTo(mEverStored)) {
1778                    throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
1779                }
1780            } catch (IOException e) {
1781                // Bad: we couldn't create the new copy.  For safety's sake we
1782                // abandon the whole process and remove all what's-backed-up
1783                // state entirely, meaning we'll force a backup pass for every
1784                // participant on the next boot or [re]install.
1785                Slog.w(TAG, "Error rewriting " + mEverStored, e);
1786                mEverStoredApps.clear();
1787                tempKnownFile.delete();
1788                mEverStored.delete();
1789            } finally {
1790                try { if (known != null) known.close(); } catch (IOException e) {}
1791            }
1792        }
1793    }
1794
1795    // Persistently record the current and ancestral backup tokens as well
1796    // as the set of packages with data [supposedly] available in the
1797    // ancestral dataset.
1798    void writeRestoreTokens() {
1799        try {
1800            RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
1801
1802            // First, the version number of this record, for futureproofing
1803            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
1804
1805            // Write the ancestral and current tokens
1806            af.writeLong(mAncestralToken);
1807            af.writeLong(mCurrentToken);
1808
1809            // Now write the set of ancestral packages
1810            if (mAncestralPackages == null) {
1811                af.writeInt(-1);
1812            } else {
1813                af.writeInt(mAncestralPackages.size());
1814                if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
1815                for (String pkgName : mAncestralPackages) {
1816                    af.writeUTF(pkgName);
1817                    if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
1818                }
1819            }
1820            af.close();
1821        } catch (IOException e) {
1822            Slog.w(TAG, "Unable to write token file:", e);
1823        }
1824    }
1825
1826    // Return the given transport
1827    private IBackupTransport getTransport(String transportName) {
1828        synchronized (mTransports) {
1829            IBackupTransport transport = mTransports.get(transportName);
1830            if (transport == null) {
1831                Slog.w(TAG, "Requested unavailable transport: " + transportName);
1832            }
1833            return transport;
1834        }
1835    }
1836
1837    // fire off a backup agent, blocking until it attaches or times out
1838    IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
1839        IBackupAgent agent = null;
1840        synchronized(mAgentConnectLock) {
1841            mConnecting = true;
1842            mConnectedAgent = null;
1843            try {
1844                if (mActivityManager.bindBackupAgent(app, mode)) {
1845                    Slog.d(TAG, "awaiting agent for " + app);
1846
1847                    // success; wait for the agent to arrive
1848                    // only wait 10 seconds for the bind to happen
1849                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1850                    while (mConnecting && mConnectedAgent == null
1851                            && (System.currentTimeMillis() < timeoutMark)) {
1852                        try {
1853                            mAgentConnectLock.wait(5000);
1854                        } catch (InterruptedException e) {
1855                            // just bail
1856                            if (DEBUG) Slog.w(TAG, "Interrupted: " + e);
1857                            mActivityManager.clearPendingBackup();
1858                            return null;
1859                        }
1860                    }
1861
1862                    // if we timed out with no connect, abort and move on
1863                    if (mConnecting == true) {
1864                        Slog.w(TAG, "Timeout waiting for agent " + app);
1865                        mActivityManager.clearPendingBackup();
1866                        return null;
1867                    }
1868                    if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
1869                    agent = mConnectedAgent;
1870                }
1871            } catch (RemoteException e) {
1872                // can't happen - ActivityManager is local
1873            }
1874        }
1875        return agent;
1876    }
1877
1878    // clear an application's data, blocking until the operation completes or times out
1879    void clearApplicationDataSynchronous(String packageName) {
1880        // Don't wipe packages marked allowClearUserData=false
1881        try {
1882            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1883            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
1884                if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
1885                        + packageName);
1886                return;
1887            }
1888        } catch (NameNotFoundException e) {
1889            Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
1890            return;
1891        }
1892
1893        ClearDataObserver observer = new ClearDataObserver();
1894
1895        synchronized(mClearDataLock) {
1896            mClearingData = true;
1897            try {
1898                mActivityManager.clearApplicationUserData(packageName, observer, 0);
1899            } catch (RemoteException e) {
1900                // can't happen because the activity manager is in this process
1901            }
1902
1903            // only wait 10 seconds for the clear data to happen
1904            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1905            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
1906                try {
1907                    mClearDataLock.wait(5000);
1908                } catch (InterruptedException e) {
1909                    // won't happen, but still.
1910                    mClearingData = false;
1911                }
1912            }
1913        }
1914    }
1915
1916    class ClearDataObserver extends IPackageDataObserver.Stub {
1917        public void onRemoveCompleted(String packageName, boolean succeeded) {
1918            synchronized(mClearDataLock) {
1919                mClearingData = false;
1920                mClearDataLock.notifyAll();
1921            }
1922        }
1923    }
1924
1925    // Get the restore-set token for the best-available restore set for this package:
1926    // the active set if possible, else the ancestral one.  Returns zero if none available.
1927    long getAvailableRestoreToken(String packageName) {
1928        long token = mAncestralToken;
1929        synchronized (mQueueLock) {
1930            if (mEverStoredApps.contains(packageName)) {
1931                token = mCurrentToken;
1932            }
1933        }
1934        return token;
1935    }
1936
1937    // -----
1938    // Interface and methods used by the asynchronous-with-timeout backup/restore operations
1939
1940    interface BackupRestoreTask {
1941        // Execute one tick of whatever state machine the task implements
1942        void execute();
1943
1944        // An operation that wanted a callback has completed
1945        void operationComplete();
1946
1947        // An operation that wanted a callback has timed out
1948        void handleTimeout();
1949    }
1950
1951    void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) {
1952        if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
1953                + " interval=" + interval);
1954        synchronized (mCurrentOpLock) {
1955            mCurrentOperations.put(token, new Operation(OP_PENDING, callback));
1956
1957            Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback);
1958            mBackupHandler.sendMessageDelayed(msg, interval);
1959        }
1960    }
1961
1962    // synchronous waiter case
1963    boolean waitUntilOperationComplete(int token) {
1964        if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
1965                + Integer.toHexString(token));
1966        int finalState = OP_PENDING;
1967        Operation op = null;
1968        synchronized (mCurrentOpLock) {
1969            while (true) {
1970                op = mCurrentOperations.get(token);
1971                if (op == null) {
1972                    // mysterious disappearance: treat as success with no callback
1973                    break;
1974                } else {
1975                    if (op.state == OP_PENDING) {
1976                        try {
1977                            mCurrentOpLock.wait();
1978                        } catch (InterruptedException e) {}
1979                        // When the wait is notified we loop around and recheck the current state
1980                    } else {
1981                        // No longer pending; we're done
1982                        finalState = op.state;
1983                        break;
1984                    }
1985                }
1986            }
1987        }
1988
1989        mBackupHandler.removeMessages(MSG_TIMEOUT);
1990        if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
1991                + " complete: finalState=" + finalState);
1992        return finalState == OP_ACKNOWLEDGED;
1993    }
1994
1995    void handleTimeout(int token, Object obj) {
1996        // Notify any synchronous waiters
1997        Operation op = null;
1998        synchronized (mCurrentOpLock) {
1999            op = mCurrentOperations.get(token);
2000            if (MORE_DEBUG) {
2001                if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token)
2002                        + " but no op found");
2003            }
2004            int state = (op != null) ? op.state : OP_TIMEOUT;
2005            if (state == OP_PENDING) {
2006                if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token));
2007                op.state = OP_TIMEOUT;
2008                mCurrentOperations.put(token, op);
2009            }
2010            mCurrentOpLock.notifyAll();
2011        }
2012
2013        // If there's a TimeoutHandler for this event, call it
2014        if (op != null && op.callback != null) {
2015            op.callback.handleTimeout();
2016        }
2017    }
2018
2019    // ----- Back up a set of applications via a worker thread -----
2020
2021    enum BackupState {
2022        INITIAL,
2023        RUNNING_QUEUE,
2024        FINAL
2025    }
2026
2027    class PerformBackupTask implements BackupRestoreTask {
2028        private static final String TAG = "PerformBackupTask";
2029
2030        IBackupTransport mTransport;
2031        ArrayList<BackupRequest> mQueue;
2032        ArrayList<BackupRequest> mOriginalQueue;
2033        File mStateDir;
2034        File mJournal;
2035        BackupState mCurrentState;
2036
2037        // carried information about the current in-flight operation
2038        PackageInfo mCurrentPackage;
2039        File mSavedStateName;
2040        File mBackupDataName;
2041        File mNewStateName;
2042        ParcelFileDescriptor mSavedState;
2043        ParcelFileDescriptor mBackupData;
2044        ParcelFileDescriptor mNewState;
2045        int mStatus;
2046        boolean mFinished;
2047
2048        public PerformBackupTask(IBackupTransport transport, String dirName,
2049                ArrayList<BackupRequest> queue, File journal) {
2050            mTransport = transport;
2051            mOriginalQueue = queue;
2052            mJournal = journal;
2053
2054            mStateDir = new File(mBaseStateDir, dirName);
2055
2056            mCurrentState = BackupState.INITIAL;
2057            mFinished = false;
2058
2059            addBackupTrace("STATE => INITIAL");
2060        }
2061
2062        // Main entry point: perform one chunk of work, updating the state as appropriate
2063        // and reposting the next chunk to the primary backup handler thread.
2064        @Override
2065        public void execute() {
2066            switch (mCurrentState) {
2067                case INITIAL:
2068                    beginBackup();
2069                    break;
2070
2071                case RUNNING_QUEUE:
2072                    invokeNextAgent();
2073                    break;
2074
2075                case FINAL:
2076                    if (!mFinished) finalizeBackup();
2077                    else {
2078                        Slog.e(TAG, "Duplicate finish");
2079                    }
2080                    mFinished = true;
2081                    break;
2082            }
2083        }
2084
2085        // We're starting a backup pass.  Initialize the transport and send
2086        // the PM metadata blob if we haven't already.
2087        void beginBackup() {
2088            if (DEBUG_BACKUP_TRACE) {
2089                clearBackupTrace();
2090                StringBuilder b = new StringBuilder(256);
2091                b.append("beginBackup: [");
2092                for (BackupRequest req : mOriginalQueue) {
2093                    b.append(' ');
2094                    b.append(req.packageName);
2095                }
2096                b.append(" ]");
2097                addBackupTrace(b.toString());
2098            }
2099
2100            mStatus = BackupConstants.TRANSPORT_OK;
2101
2102            // Sanity check: if the queue is empty we have no work to do.
2103            if (mOriginalQueue.isEmpty()) {
2104                Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
2105                addBackupTrace("queue empty at begin");
2106                executeNextState(BackupState.FINAL);
2107                return;
2108            }
2109
2110            // We need to retain the original queue contents in case of transport
2111            // failure, but we want a working copy that we can manipulate along
2112            // the way.
2113            mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
2114
2115            if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
2116
2117            File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2118            try {
2119                final String transportName = mTransport.transportDirName();
2120                EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
2121
2122                // If we haven't stored package manager metadata yet, we must init the transport.
2123                if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
2124                    Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
2125                    addBackupTrace("initializing transport " + transportName);
2126                    resetBackupState(mStateDir);  // Just to make sure.
2127                    mStatus = mTransport.initializeDevice();
2128
2129                    addBackupTrace("transport.initializeDevice() == " + mStatus);
2130                    if (mStatus == BackupConstants.TRANSPORT_OK) {
2131                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
2132                    } else {
2133                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
2134                        Slog.e(TAG, "Transport error in initializeDevice()");
2135                    }
2136                }
2137
2138                // The package manager doesn't have a proper <application> etc, but since
2139                // it's running here in the system process we can just set up its agent
2140                // directly and use a synthetic BackupRequest.  We always run this pass
2141                // because it's cheap and this way we guarantee that we don't get out of
2142                // step even if we're selecting among various transports at run time.
2143                if (mStatus == BackupConstants.TRANSPORT_OK) {
2144                    PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
2145                            mPackageManager, allAgentPackages());
2146                    mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
2147                            IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
2148                    addBackupTrace("PMBA invoke: " + mStatus);
2149                }
2150
2151                if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
2152                    // The backend reports that our dataset has been wiped.  Note this in
2153                    // the event log; the no-success code below will reset the backup
2154                    // state as well.
2155                    EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
2156                }
2157            } catch (Exception e) {
2158                Slog.e(TAG, "Error in backup thread", e);
2159                addBackupTrace("Exception in backup thread: " + e);
2160                mStatus = BackupConstants.TRANSPORT_ERROR;
2161            } finally {
2162                // If we've succeeded so far, invokeAgentForBackup() will have run the PM
2163                // metadata and its completion/timeout callback will continue the state
2164                // machine chain.  If it failed that won't happen; we handle that now.
2165                addBackupTrace("exiting prelim: " + mStatus);
2166                if (mStatus != BackupConstants.TRANSPORT_OK) {
2167                    // if things went wrong at this point, we need to
2168                    // restage everything and try again later.
2169                    resetBackupState(mStateDir);  // Just to make sure.
2170                    executeNextState(BackupState.FINAL);
2171                }
2172            }
2173        }
2174
2175        // Transport has been initialized and the PM metadata submitted successfully
2176        // if that was warranted.  Now we process the single next thing in the queue.
2177        void invokeNextAgent() {
2178            mStatus = BackupConstants.TRANSPORT_OK;
2179            addBackupTrace("invoke q=" + mQueue.size());
2180
2181            // Sanity check that we have work to do.  If not, skip to the end where
2182            // we reestablish the wakelock invariants etc.
2183            if (mQueue.isEmpty()) {
2184                if (DEBUG) Slog.i(TAG, "queue now empty");
2185                executeNextState(BackupState.FINAL);
2186                return;
2187            }
2188
2189            // pop the entry we're going to process on this step
2190            BackupRequest request = mQueue.get(0);
2191            mQueue.remove(0);
2192
2193            Slog.d(TAG, "starting agent for backup of " + request);
2194            addBackupTrace("launch agent for " + request.packageName);
2195
2196            // Verify that the requested app exists; it might be something that
2197            // requested a backup but was then uninstalled.  The request was
2198            // journalled and rather than tamper with the journal it's safer
2199            // to sanity-check here.  This also gives us the classname of the
2200            // package's backup agent.
2201            try {
2202                mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
2203                        PackageManager.GET_SIGNATURES);
2204                if (mCurrentPackage.applicationInfo.backupAgentName == null) {
2205                    // The manifest has changed but we had a stale backup request pending.
2206                    // This won't happen again because the app won't be requesting further
2207                    // backups.
2208                    Slog.i(TAG, "Package " + request.packageName
2209                            + " no longer supports backup; skipping");
2210                    addBackupTrace("skipping - no agent, completion is noop");
2211                    executeNextState(BackupState.RUNNING_QUEUE);
2212                    return;
2213                }
2214
2215                if ((mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
2216                    // The app has been force-stopped or cleared or just installed,
2217                    // and not yet launched out of that state, so just as it won't
2218                    // receive broadcasts, we won't run it for backup.
2219                    addBackupTrace("skipping - stopped");
2220                    executeNextState(BackupState.RUNNING_QUEUE);
2221                    return;
2222                }
2223
2224                IBackupAgent agent = null;
2225                try {
2226                    mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
2227                    agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
2228                            IApplicationThread.BACKUP_MODE_INCREMENTAL);
2229                    addBackupTrace("agent bound; a? = " + (agent != null));
2230                    if (agent != null) {
2231                        mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
2232                        // at this point we'll either get a completion callback from the
2233                        // agent, or a timeout message on the main handler.  either way, we're
2234                        // done here as long as we're successful so far.
2235                    } else {
2236                        // Timeout waiting for the agent
2237                        mStatus = BackupConstants.AGENT_ERROR;
2238                    }
2239                } catch (SecurityException ex) {
2240                    // Try for the next one.
2241                    Slog.d(TAG, "error in bind/backup", ex);
2242                    mStatus = BackupConstants.AGENT_ERROR;
2243                            addBackupTrace("agent SE");
2244                }
2245            } catch (NameNotFoundException e) {
2246                Slog.d(TAG, "Package does not exist; skipping");
2247                addBackupTrace("no such package");
2248                mStatus = BackupConstants.AGENT_UNKNOWN;
2249            } finally {
2250                mWakelock.setWorkSource(null);
2251
2252                // If there was an agent error, no timeout/completion handling will occur.
2253                // That means we need to direct to the next state ourselves.
2254                if (mStatus != BackupConstants.TRANSPORT_OK) {
2255                    BackupState nextState = BackupState.RUNNING_QUEUE;
2256
2257                    // An agent-level failure means we reenqueue this one agent for
2258                    // a later retry, but otherwise proceed normally.
2259                    if (mStatus == BackupConstants.AGENT_ERROR) {
2260                        if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
2261                                + " - restaging");
2262                        dataChangedImpl(request.packageName);
2263                        mStatus = BackupConstants.TRANSPORT_OK;
2264                        if (mQueue.isEmpty()) nextState = BackupState.FINAL;
2265                    } else if (mStatus == BackupConstants.AGENT_UNKNOWN) {
2266                        // Failed lookup of the app, so we couldn't bring up an agent, but
2267                        // we're otherwise fine.  Just drop it and go on to the next as usual.
2268                        mStatus = BackupConstants.TRANSPORT_OK;
2269                    } else {
2270                        // Transport-level failure means we reenqueue everything
2271                        revertAndEndBackup();
2272                        nextState = BackupState.FINAL;
2273                    }
2274
2275                    executeNextState(nextState);
2276                } else {
2277                    addBackupTrace("expecting completion/timeout callback");
2278                }
2279            }
2280        }
2281
2282        void finalizeBackup() {
2283            addBackupTrace("finishing");
2284
2285            // Either backup was successful, in which case we of course do not need
2286            // this pass's journal any more; or it failed, in which case we just
2287            // re-enqueued all of these packages in the current active journal.
2288            // Either way, we no longer need this pass's journal.
2289            if (mJournal != null && !mJournal.delete()) {
2290                Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
2291            }
2292
2293            // If everything actually went through and this is the first time we've
2294            // done a backup, we can now record what the current backup dataset token
2295            // is.
2296            if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) {
2297                addBackupTrace("success; recording token");
2298                try {
2299                    mCurrentToken = mTransport.getCurrentRestoreSet();
2300                    writeRestoreTokens();
2301                } catch (RemoteException e) {
2302                    // nothing for it at this point, unfortunately, but this will be
2303                    // recorded the next time we fully succeed.
2304                    addBackupTrace("transport threw returning token");
2305                }
2306            }
2307
2308            // Set up the next backup pass - at this point we can set mBackupRunning
2309            // to false to allow another pass to fire, because we're done with the
2310            // state machine sequence and the wakelock is refcounted.
2311            synchronized (mQueueLock) {
2312                mBackupRunning = false;
2313                if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
2314                    // Make sure we back up everything and perform the one-time init
2315                    clearMetadata();
2316                    if (DEBUG) Slog.d(TAG, "Server requires init; rerunning");
2317                    addBackupTrace("init required; rerunning");
2318                    backupNow();
2319                }
2320            }
2321
2322            // Only once we're entirely finished do we release the wakelock
2323            clearBackupTrace();
2324            Slog.i(TAG, "Backup pass finished.");
2325            mWakelock.release();
2326        }
2327
2328        // Remove the PM metadata state. This will generate an init on the next pass.
2329        void clearMetadata() {
2330            final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2331            if (pmState.exists()) pmState.delete();
2332        }
2333
2334        // Invoke an agent's doBackup() and start a timeout message spinning on the main
2335        // handler in case it doesn't get back to us.
2336        int invokeAgentForBackup(String packageName, IBackupAgent agent,
2337                IBackupTransport transport) {
2338            if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName);
2339            addBackupTrace("invoking " + packageName);
2340
2341            mSavedStateName = new File(mStateDir, packageName);
2342            mBackupDataName = new File(mDataDir, packageName + ".data");
2343            mNewStateName = new File(mStateDir, packageName + ".new");
2344            if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName);
2345
2346            mSavedState = null;
2347            mBackupData = null;
2348            mNewState = null;
2349
2350            final int token = generateToken();
2351            try {
2352                // Look up the package info & signatures.  This is first so that if it
2353                // throws an exception, there's no file setup yet that would need to
2354                // be unraveled.
2355                if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
2356                    // The metadata 'package' is synthetic; construct one and make
2357                    // sure our global state is pointed at it
2358                    mCurrentPackage = new PackageInfo();
2359                    mCurrentPackage.packageName = packageName;
2360                }
2361
2362                // In a full backup, we pass a null ParcelFileDescriptor as
2363                // the saved-state "file". This is by definition an incremental,
2364                // so we build a saved state file to pass.
2365                mSavedState = ParcelFileDescriptor.open(mSavedStateName,
2366                        ParcelFileDescriptor.MODE_READ_ONLY |
2367                        ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
2368
2369                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
2370                        ParcelFileDescriptor.MODE_READ_WRITE |
2371                        ParcelFileDescriptor.MODE_CREATE |
2372                        ParcelFileDescriptor.MODE_TRUNCATE);
2373
2374                if (!SELinux.restorecon(mBackupDataName)) {
2375                    Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
2376                }
2377
2378                mNewState = ParcelFileDescriptor.open(mNewStateName,
2379                        ParcelFileDescriptor.MODE_READ_WRITE |
2380                        ParcelFileDescriptor.MODE_CREATE |
2381                        ParcelFileDescriptor.MODE_TRUNCATE);
2382
2383                // Initiate the target's backup pass
2384                addBackupTrace("setting timeout");
2385                prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
2386                addBackupTrace("calling agent doBackup()");
2387                agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder);
2388            } catch (Exception e) {
2389                Slog.e(TAG, "Error invoking for backup on " + packageName);
2390                addBackupTrace("exception: " + e);
2391                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
2392                        e.toString());
2393                agentErrorCleanup();
2394                return BackupConstants.AGENT_ERROR;
2395            }
2396
2397            // At this point the agent is off and running.  The next thing to happen will
2398            // either be a callback from the agent, at which point we'll process its data
2399            // for transport, or a timeout.  Either way the next phase will happen in
2400            // response to the TimeoutHandler interface callbacks.
2401            addBackupTrace("invoke success");
2402            return BackupConstants.TRANSPORT_OK;
2403        }
2404
2405        @Override
2406        public void operationComplete() {
2407            // Okay, the agent successfully reported back to us.  The next thing we do is
2408            // push the app widget state for the app, if any.
2409            final String pkgName = mCurrentPackage.packageName;
2410            final long filepos = mBackupDataName.length();
2411            FileDescriptor fd = mBackupData.getFileDescriptor();
2412            try {
2413                BackupDataOutput out = new BackupDataOutput(fd);
2414                byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
2415                        UserHandle.USER_OWNER);
2416                if (widgetState != null) {
2417                    out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
2418                    out.writeEntityData(widgetState, widgetState.length);
2419                } else {
2420                    // No widget state for this app, but push a 'delete' operation for it
2421                    // in case they're trying to play games with the payload.
2422                    out.writeEntityHeader(KEY_WIDGET_STATE, -1);
2423                }
2424            } catch (IOException e) {
2425                // Hard disk error; recovery/failure policy TBD.  For now roll back,
2426                // but we may want to consider this a transport-level failure (i.e.
2427                // we're in such a bad state that we can't contemplate doing backup
2428                // operations any more during this pass).
2429                Slog.w(TAG, "Unable to save widget state for " + pkgName);
2430                try {
2431                    Libcore.os.ftruncate(fd, filepos);
2432                } catch (ErrnoException ee) {
2433                    Slog.w(TAG, "Unable to roll back!");
2434                }
2435            }
2436
2437            // Spin the data off to the
2438            // transport and proceed with the next stage.
2439            if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
2440                    + pkgName);
2441            mBackupHandler.removeMessages(MSG_TIMEOUT);
2442            clearAgentState();
2443            addBackupTrace("operation complete");
2444
2445            ParcelFileDescriptor backupData = null;
2446            mStatus = BackupConstants.TRANSPORT_OK;
2447            try {
2448                int size = (int) mBackupDataName.length();
2449                if (size > 0) {
2450                    if (mStatus == BackupConstants.TRANSPORT_OK) {
2451                        backupData = ParcelFileDescriptor.open(mBackupDataName,
2452                                ParcelFileDescriptor.MODE_READ_ONLY);
2453                        addBackupTrace("sending data to transport");
2454                        mStatus = mTransport.performBackup(mCurrentPackage, backupData);
2455                    }
2456
2457                    // TODO - We call finishBackup() for each application backed up, because
2458                    // we need to know now whether it succeeded or failed.  Instead, we should
2459                    // hold off on finishBackup() until the end, which implies holding off on
2460                    // renaming *all* the output state files (see below) until that happens.
2461
2462                    addBackupTrace("data delivered: " + mStatus);
2463                    if (mStatus == BackupConstants.TRANSPORT_OK) {
2464                        addBackupTrace("finishing op on transport");
2465                        mStatus = mTransport.finishBackup();
2466                        addBackupTrace("finished: " + mStatus);
2467                    }
2468                } else {
2469                    if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
2470                    addBackupTrace("no data to send");
2471                }
2472
2473                // After successful transport, delete the now-stale data
2474                // and juggle the files so that next time we supply the agent
2475                // with the new state file it just created.
2476                if (mStatus == BackupConstants.TRANSPORT_OK) {
2477                    mBackupDataName.delete();
2478                    mNewStateName.renameTo(mSavedStateName);
2479                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
2480                    logBackupComplete(pkgName);
2481                } else {
2482                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
2483                }
2484            } catch (Exception e) {
2485                Slog.e(TAG, "Transport error backing up " + pkgName, e);
2486                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
2487                mStatus = BackupConstants.TRANSPORT_ERROR;
2488            } finally {
2489                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
2490            }
2491
2492            // If we encountered an error here it's a transport-level failure.  That
2493            // means we need to halt everything and reschedule everything for next time.
2494            final BackupState nextState;
2495            if (mStatus != BackupConstants.TRANSPORT_OK) {
2496                revertAndEndBackup();
2497                nextState = BackupState.FINAL;
2498            } else {
2499                // Success!  Proceed with the next app if any, otherwise we're done.
2500                nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
2501            }
2502
2503            executeNextState(nextState);
2504        }
2505
2506        @Override
2507        public void handleTimeout() {
2508            // Whoops, the current agent timed out running doBackup().  Tidy up and restage
2509            // it for the next time we run a backup pass.
2510            // !!! TODO: keep track of failure counts per agent, and blacklist those which
2511            // fail repeatedly (i.e. have proved themselves to be buggy).
2512            Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
2513            EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
2514                    "timeout");
2515            addBackupTrace("timeout of " + mCurrentPackage.packageName);
2516            agentErrorCleanup();
2517            dataChangedImpl(mCurrentPackage.packageName);
2518        }
2519
2520        void revertAndEndBackup() {
2521            if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
2522            addBackupTrace("transport error; reverting");
2523            for (BackupRequest request : mOriginalQueue) {
2524                dataChangedImpl(request.packageName);
2525            }
2526            // We also want to reset the backup schedule based on whatever
2527            // the transport suggests by way of retry/backoff time.
2528            restartBackupAlarm();
2529        }
2530
2531        void agentErrorCleanup() {
2532            mBackupDataName.delete();
2533            mNewStateName.delete();
2534            clearAgentState();
2535
2536            executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
2537        }
2538
2539        // Cleanup common to both success and failure cases
2540        void clearAgentState() {
2541            try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {}
2542            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
2543            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
2544            mSavedState = mBackupData = mNewState = null;
2545            synchronized (mCurrentOpLock) {
2546                mCurrentOperations.clear();
2547            }
2548
2549            // If this was a pseudopackage there's no associated Activity Manager state
2550            if (mCurrentPackage.applicationInfo != null) {
2551                addBackupTrace("unbinding " + mCurrentPackage.packageName);
2552                try {  // unbind even on timeout, just in case
2553                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
2554                } catch (RemoteException e) { /* can't happen; activity manager is local */ }
2555            }
2556        }
2557
2558        void restartBackupAlarm() {
2559            addBackupTrace("setting backup trigger");
2560            synchronized (mQueueLock) {
2561                try {
2562                    startBackupAlarmsLocked(mTransport.requestBackupTime());
2563                } catch (RemoteException e) { /* cannot happen */ }
2564            }
2565        }
2566
2567        void executeNextState(BackupState nextState) {
2568            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
2569                    + this + " nextState=" + nextState);
2570            addBackupTrace("executeNextState => " + nextState);
2571            mCurrentState = nextState;
2572            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
2573            mBackupHandler.sendMessage(msg);
2574        }
2575    }
2576
2577
2578    // ----- Full backup/restore to a file/socket -----
2579
2580    abstract class ObbServiceClient {
2581        public IObbBackupService mObbService;
2582        public void setObbBinder(IObbBackupService binder) {
2583            mObbService = binder;
2584        }
2585    }
2586
2587    class FullBackupObbConnection implements ServiceConnection {
2588        volatile IObbBackupService mService;
2589
2590        FullBackupObbConnection() {
2591            mService = null;
2592        }
2593
2594        public void establish() {
2595            if (DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this);
2596            Intent obbIntent = new Intent().setComponent(new ComponentName(
2597                    "com.android.sharedstoragebackup",
2598                    "com.android.sharedstoragebackup.ObbBackupService"));
2599            BackupManagerService.this.mContext.bindService(
2600                    obbIntent, this, Context.BIND_AUTO_CREATE);
2601        }
2602
2603        public void tearDown() {
2604            BackupManagerService.this.mContext.unbindService(this);
2605        }
2606
2607        public boolean backupObbs(PackageInfo pkg, OutputStream out) {
2608            boolean success = false;
2609            waitForConnection();
2610
2611            ParcelFileDescriptor[] pipes = null;
2612            try {
2613                pipes = ParcelFileDescriptor.createPipe();
2614                int token = generateToken();
2615                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
2616                mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
2617                routeSocketDataToOutput(pipes[0], out);
2618                success = waitUntilOperationComplete(token);
2619            } catch (Exception e) {
2620                Slog.w(TAG, "Unable to back up OBBs for " + pkg, e);
2621            } finally {
2622                try {
2623                    out.flush();
2624                    if (pipes != null) {
2625                        if (pipes[0] != null) pipes[0].close();
2626                        if (pipes[1] != null) pipes[1].close();
2627                    }
2628                } catch (IOException e) {
2629                    Slog.w(TAG, "I/O error closing down OBB backup", e);
2630                }
2631            }
2632            return success;
2633        }
2634
2635        public void restoreObbFile(String pkgName, ParcelFileDescriptor data,
2636                long fileSize, int type, String path, long mode, long mtime,
2637                int token, IBackupManager callbackBinder) {
2638            waitForConnection();
2639
2640            try {
2641                mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime,
2642                        token, callbackBinder);
2643            } catch (Exception e) {
2644                Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e);
2645            }
2646        }
2647
2648        private void waitForConnection() {
2649            synchronized (this) {
2650                while (mService == null) {
2651                    if (DEBUG) Slog.i(TAG, "...waiting for OBB service binding...");
2652                    try {
2653                        this.wait();
2654                    } catch (InterruptedException e) { /* never interrupted */ }
2655                }
2656                if (DEBUG) Slog.i(TAG, "Connected to OBB service; continuing");
2657            }
2658        }
2659
2660        @Override
2661        public void onServiceConnected(ComponentName name, IBinder service) {
2662            synchronized (this) {
2663                mService = IObbBackupService.Stub.asInterface(service);
2664                if (DEBUG) Slog.i(TAG, "OBB service connection " + mService
2665                        + " connected on " + this);
2666                this.notifyAll();
2667            }
2668        }
2669
2670        @Override
2671        public void onServiceDisconnected(ComponentName name) {
2672            synchronized (this) {
2673                mService = null;
2674                if (DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this);
2675                this.notifyAll();
2676            }
2677        }
2678
2679    }
2680
2681    private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
2682            throws IOException {
2683        FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
2684        DataInputStream in = new DataInputStream(raw);
2685
2686        byte[] buffer = new byte[32 * 1024];
2687        int chunkTotal;
2688        while ((chunkTotal = in.readInt()) > 0) {
2689            while (chunkTotal > 0) {
2690                int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
2691                int nRead = in.read(buffer, 0, toRead);
2692                out.write(buffer, 0, nRead);
2693                chunkTotal -= nRead;
2694            }
2695        }
2696    }
2697
2698    class PerformFullBackupTask extends ObbServiceClient implements Runnable {
2699        ParcelFileDescriptor mOutputFile;
2700        DeflaterOutputStream mDeflater;
2701        IFullBackupRestoreObserver mObserver;
2702        boolean mIncludeApks;
2703        boolean mIncludeObbs;
2704        boolean mIncludeShared;
2705        boolean mDoWidgets;
2706        boolean mAllApps;
2707        final boolean mIncludeSystem;
2708        ArrayList<String> mPackages;
2709        String mCurrentPassword;
2710        String mEncryptPassword;
2711        AtomicBoolean mLatchObject;
2712        File mFilesDir;
2713        File mManifestFile;
2714        File mMetadataFile;
2715
2716
2717        class FullBackupRunner implements Runnable {
2718            PackageInfo mPackage;
2719            byte[] mWidgetData;
2720            IBackupAgent mAgent;
2721            ParcelFileDescriptor mPipe;
2722            int mToken;
2723            boolean mSendApk;
2724            boolean mWriteManifest;
2725
2726            FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
2727                    int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
2728                            throws IOException {
2729                mPackage = pack;
2730                mWidgetData = widgetData;
2731                mAgent = agent;
2732                mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
2733                mToken = token;
2734                mSendApk = sendApk;
2735                mWriteManifest = writeManifest;
2736            }
2737
2738            @Override
2739            public void run() {
2740                try {
2741                    BackupDataOutput output = new BackupDataOutput(
2742                            mPipe.getFileDescriptor());
2743
2744                    if (mWriteManifest) {
2745                        final boolean writeWidgetData = mWidgetData != null;
2746                        if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
2747                        writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
2748                        FullBackup.backupToTar(mPackage.packageName, null, null,
2749                                mFilesDir.getAbsolutePath(),
2750                                mManifestFile.getAbsolutePath(),
2751                                output);
2752                        mManifestFile.delete();
2753
2754                        // We only need to write a metadata file if we have widget data to stash
2755                        if (writeWidgetData) {
2756                            writeMetadata(mPackage, mMetadataFile, mWidgetData);
2757                            FullBackup.backupToTar(mPackage.packageName, null, null,
2758                                    mFilesDir.getAbsolutePath(),
2759                                    mMetadataFile.getAbsolutePath(),
2760                                    output);
2761                            mMetadataFile.delete();
2762                        }
2763                    }
2764
2765                    if (mSendApk) {
2766                        writeApkToBackup(mPackage, output);
2767                    }
2768
2769                    if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
2770                    prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null);
2771                    mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
2772                } catch (IOException e) {
2773                    Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
2774                } catch (RemoteException e) {
2775                    Slog.e(TAG, "Remote agent vanished during full backup of "
2776                            + mPackage.packageName);
2777                } finally {
2778                    try {
2779                        mPipe.close();
2780                    } catch (IOException e) {}
2781                }
2782            }
2783        }
2784
2785        PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
2786                boolean includeApks, boolean includeObbs, boolean includeShared,
2787                boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
2788                boolean doSystem, String[] packages, AtomicBoolean latch) {
2789            mOutputFile = fd;
2790            mObserver = observer;
2791            mIncludeApks = includeApks;
2792            mIncludeObbs = includeObbs;
2793            mIncludeShared = includeShared;
2794            mDoWidgets = doWidgets;
2795            mAllApps = doAllApps;
2796            mIncludeSystem = doSystem;
2797            mPackages = (packages == null)
2798                    ? new ArrayList<String>()
2799                    : new ArrayList<String>(Arrays.asList(packages));
2800            mCurrentPassword = curPassword;
2801            // when backing up, if there is a current backup password, we require that
2802            // the user use a nonempty encryption password as well.  if one is supplied
2803            // in the UI we use that, but if the UI was left empty we fall back to the
2804            // current backup password (which was supplied by the user as well).
2805            if (encryptPassword == null || "".equals(encryptPassword)) {
2806                mEncryptPassword = curPassword;
2807            } else {
2808                mEncryptPassword = encryptPassword;
2809            }
2810            mLatchObject = latch;
2811
2812            mFilesDir = new File("/data/system");
2813            mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
2814            mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
2815        }
2816
2817        void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
2818            for (String pkgName : pkgNames) {
2819                if (!set.containsKey(pkgName)) {
2820                    try {
2821                        PackageInfo info = mPackageManager.getPackageInfo(pkgName,
2822                                PackageManager.GET_SIGNATURES);
2823                        set.put(pkgName, info);
2824                    } catch (NameNotFoundException e) {
2825                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
2826                    }
2827                }
2828            }
2829        }
2830
2831        @Override
2832        public void run() {
2833            Slog.i(TAG, "--- Performing full-dataset backup ---");
2834
2835            TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
2836            FullBackupObbConnection obbConnection = new FullBackupObbConnection();
2837            obbConnection.establish();  // we'll want this later
2838
2839            sendStartBackup();
2840
2841            // doAllApps supersedes the package set if any
2842            if (mAllApps) {
2843                List<PackageInfo> allPackages = mPackageManager.getInstalledPackages(
2844                        PackageManager.GET_SIGNATURES);
2845                for (int i = 0; i < allPackages.size(); i++) {
2846                    PackageInfo pkg = allPackages.get(i);
2847                    // Exclude system apps if we've been asked to do so
2848                    if (mIncludeSystem == true
2849                            || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
2850                        packagesToBackup.put(pkg.packageName, pkg);
2851                    }
2852                }
2853            }
2854
2855            // If we're doing widget state as well, ensure that we have all the involved
2856            // host & provider packages in the set
2857            if (mDoWidgets) {
2858                List<String> pkgs =
2859                        AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_OWNER);
2860                if (pkgs != null) {
2861                    if (MORE_DEBUG) {
2862                        Slog.i(TAG, "Adding widget participants to backup set:");
2863                        StringBuilder sb = new StringBuilder(128);
2864                        sb.append("   ");
2865                        for (String s : pkgs) {
2866                            sb.append(' ');
2867                            sb.append(s);
2868                        }
2869                        Slog.i(TAG, sb.toString());
2870                    }
2871                    addPackagesToSet(packagesToBackup, pkgs);
2872                }
2873            }
2874
2875            // Now process the command line argument packages, if any. Note that explicitly-
2876            // named system-partition packages will be included even if includeSystem was
2877            // set to false.
2878            if (mPackages != null) {
2879                addPackagesToSet(packagesToBackup, mPackages);
2880            }
2881
2882            // Now we cull any inapplicable / inappropriate packages from the set
2883            Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
2884            while (iter.hasNext()) {
2885                PackageInfo pkg = iter.next().getValue();
2886                if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0
2887                        || pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) {
2888                    // Cull any packages that have indicated that backups are not permitted, as well
2889                    // as any explicit mention of the 'special' shared-storage agent package (we
2890                    // handle that one at the end).
2891                    iter.remove();
2892                } else if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
2893                        && (pkg.applicationInfo.backupAgentName == null)) {
2894                    // Cull any packages that run as system-domain uids but do not define their
2895                    // own backup agents
2896                    if (MORE_DEBUG) {
2897                        Slog.i(TAG, "... ignoring non-agent system package " + pkg.packageName);
2898                    }
2899                    iter.remove();
2900                }
2901            }
2902
2903            // flatten the set of packages now so we can explicitly control the ordering
2904            ArrayList<PackageInfo> backupQueue =
2905                    new ArrayList<PackageInfo>(packagesToBackup.values());
2906
2907            FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
2908            OutputStream out = null;
2909
2910            PackageInfo pkg = null;
2911            try {
2912                boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
2913                boolean compressing = COMPRESS_FULL_BACKUPS;
2914                OutputStream finalOutput = ofstream;
2915
2916                // Verify that the given password matches the currently-active
2917                // backup password, if any
2918                if (!backupPasswordMatches(mCurrentPassword)) {
2919                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
2920                    return;
2921                }
2922
2923                // Write the global file header.  All strings are UTF-8 encoded; lines end
2924                // with a '\n' byte.  Actual backup data begins immediately following the
2925                // final '\n'.
2926                //
2927                // line 1: "ANDROID BACKUP"
2928                // line 2: backup file format version, currently "2"
2929                // line 3: compressed?  "0" if not compressed, "1" if compressed.
2930                // line 4: name of encryption algorithm [currently only "none" or "AES-256"]
2931                //
2932                // When line 4 is not "none", then additional header data follows:
2933                //
2934                // line 5: user password salt [hex]
2935                // line 6: master key checksum salt [hex]
2936                // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal]
2937                // line 8: IV of the user key [hex]
2938                // line 9: master key blob [hex]
2939                //     IV of the master key, master key itself, master key checksum hash
2940                //
2941                // The master key checksum is the master key plus its checksum salt, run through
2942                // 10k rounds of PBKDF2.  This is used to verify that the user has supplied the
2943                // correct password for decrypting the archive:  the master key decrypted from
2944                // the archive using the user-supplied password is also run through PBKDF2 in
2945                // this way, and if the result does not match the checksum as stored in the
2946                // archive, then we know that the user-supplied password does not match the
2947                // archive's.
2948                StringBuilder headerbuf = new StringBuilder(1024);
2949
2950                headerbuf.append(BACKUP_FILE_HEADER_MAGIC);
2951                headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n
2952                headerbuf.append(compressing ? "\n1\n" : "\n0\n");
2953
2954                try {
2955                    // Set up the encryption stage if appropriate, and emit the correct header
2956                    if (encrypting) {
2957                        finalOutput = emitAesBackupHeader(headerbuf, finalOutput);
2958                    } else {
2959                        headerbuf.append("none\n");
2960                    }
2961
2962                    byte[] header = headerbuf.toString().getBytes("UTF-8");
2963                    ofstream.write(header);
2964
2965                    // Set up the compression stage feeding into the encryption stage (if any)
2966                    if (compressing) {
2967                        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
2968                        finalOutput = new DeflaterOutputStream(finalOutput, deflater, true);
2969                    }
2970
2971                    out = finalOutput;
2972                } catch (Exception e) {
2973                    // Should never happen!
2974                    Slog.e(TAG, "Unable to emit archive header", e);
2975                    return;
2976                }
2977
2978                // Shared storage if requested
2979                if (mIncludeShared) {
2980                    try {
2981                        pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
2982                        backupQueue.add(pkg);
2983                    } catch (NameNotFoundException e) {
2984                        Slog.e(TAG, "Unable to find shared-storage backup handler");
2985                    }
2986                }
2987
2988                // Now back up the app data via the agent mechanism
2989                int N = backupQueue.size();
2990                for (int i = 0; i < N; i++) {
2991                    pkg = backupQueue.get(i);
2992                    backupOnePackage(pkg, out);
2993
2994                    // after the app's agent runs to handle its private filesystem
2995                    // contents, back up any OBB content it has on its behalf.
2996                    if (mIncludeObbs) {
2997                        boolean obbOkay = obbConnection.backupObbs(pkg, out);
2998                        if (!obbOkay) {
2999                            throw new RuntimeException("Failure writing OBB stack for " + pkg);
3000                        }
3001                    }
3002                }
3003
3004                // Done!
3005                finalizeBackup(out);
3006            } catch (RemoteException e) {
3007                Slog.e(TAG, "App died during full backup");
3008            } catch (Exception e) {
3009                Slog.e(TAG, "Internal exception during full backup", e);
3010            } finally {
3011                tearDown(pkg);
3012                try {
3013                    if (out != null) out.close();
3014                    mOutputFile.close();
3015                } catch (IOException e) {
3016                    /* nothing we can do about this */
3017                }
3018                synchronized (mCurrentOpLock) {
3019                    mCurrentOperations.clear();
3020                }
3021                synchronized (mLatchObject) {
3022                    mLatchObject.set(true);
3023                    mLatchObject.notifyAll();
3024                }
3025                sendEndBackup();
3026                obbConnection.tearDown();
3027                if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
3028                mWakelock.release();
3029            }
3030        }
3031
3032        private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
3033                OutputStream ofstream) throws Exception {
3034            // User key will be used to encrypt the master key.
3035            byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE);
3036            SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt,
3037                    PBKDF2_HASH_ROUNDS);
3038
3039            // the master key is random for each backup
3040            byte[] masterPw = new byte[256 / 8];
3041            mRng.nextBytes(masterPw);
3042            byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE);
3043
3044            // primary encryption of the datastream with the random key
3045            Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
3046            SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES");
3047            c.init(Cipher.ENCRYPT_MODE, masterKeySpec);
3048            OutputStream finalOutput = new CipherOutputStream(ofstream, c);
3049
3050            // line 4: name of encryption algorithm
3051            headerbuf.append(ENCRYPTION_ALGORITHM_NAME);
3052            headerbuf.append('\n');
3053            // line 5: user password salt [hex]
3054            headerbuf.append(byteArrayToHex(newUserSalt));
3055            headerbuf.append('\n');
3056            // line 6: master key checksum salt [hex]
3057            headerbuf.append(byteArrayToHex(checksumSalt));
3058            headerbuf.append('\n');
3059            // line 7: number of PBKDF2 rounds used [decimal]
3060            headerbuf.append(PBKDF2_HASH_ROUNDS);
3061            headerbuf.append('\n');
3062
3063            // line 8: IV of the user key [hex]
3064            Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding");
3065            mkC.init(Cipher.ENCRYPT_MODE, userKey);
3066
3067            byte[] IV = mkC.getIV();
3068            headerbuf.append(byteArrayToHex(IV));
3069            headerbuf.append('\n');
3070
3071            // line 9: master IV + key blob, encrypted by the user key [hex].  Blob format:
3072            //    [byte] IV length = Niv
3073            //    [array of Niv bytes] IV itself
3074            //    [byte] master key length = Nmk
3075            //    [array of Nmk bytes] master key itself
3076            //    [byte] MK checksum hash length = Nck
3077            //    [array of Nck bytes] master key checksum hash
3078            //
3079            // The checksum is the (master key + checksum salt), run through the
3080            // stated number of PBKDF2 rounds
3081            IV = c.getIV();
3082            byte[] mk = masterKeySpec.getEncoded();
3083            byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(),
3084                    checksumSalt, PBKDF2_HASH_ROUNDS);
3085
3086            ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length
3087                    + checksum.length + 3);
3088            DataOutputStream mkOut = new DataOutputStream(blob);
3089            mkOut.writeByte(IV.length);
3090            mkOut.write(IV);
3091            mkOut.writeByte(mk.length);
3092            mkOut.write(mk);
3093            mkOut.writeByte(checksum.length);
3094            mkOut.write(checksum);
3095            mkOut.flush();
3096            byte[] encryptedMk = mkC.doFinal(blob.toByteArray());
3097            headerbuf.append(byteArrayToHex(encryptedMk));
3098            headerbuf.append('\n');
3099
3100            return finalOutput;
3101        }
3102
3103        private void backupOnePackage(PackageInfo pkg, OutputStream out)
3104                throws RemoteException {
3105            Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
3106
3107            IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
3108                    IApplicationThread.BACKUP_MODE_FULL);
3109            if (agent != null) {
3110                ParcelFileDescriptor[] pipes = null;
3111                try {
3112                    pipes = ParcelFileDescriptor.createPipe();
3113
3114                    ApplicationInfo app = pkg.applicationInfo;
3115                    final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
3116                    final boolean sendApk = mIncludeApks
3117                            && !isSharedStorage
3118                            && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
3119                            && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
3120                                (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
3121
3122                    byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
3123                            UserHandle.USER_OWNER);
3124                    sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
3125
3126                    final int token = generateToken();
3127                    FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
3128                            token, sendApk, !isSharedStorage, widgetBlob);
3129                    pipes[1].close();   // the runner has dup'd it
3130                    pipes[1] = null;
3131                    Thread t = new Thread(runner);
3132                    t.start();
3133
3134                    // Now pull data from the app and stuff it into the compressor
3135                    try {
3136                        routeSocketDataToOutput(pipes[0], out);
3137                    } catch (IOException e) {
3138                        Slog.i(TAG, "Caught exception reading from agent", e);
3139                    }
3140
3141                    if (!waitUntilOperationComplete(token)) {
3142                        Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
3143                    } else {
3144                        if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
3145                    }
3146
3147                } catch (IOException e) {
3148                    Slog.e(TAG, "Error backing up " + pkg.packageName, e);
3149                } finally {
3150                    try {
3151                        // flush after every package
3152                        out.flush();
3153                        if (pipes != null) {
3154                            if (pipes[0] != null) pipes[0].close();
3155                            if (pipes[1] != null) pipes[1].close();
3156                        }
3157                    } catch (IOException e) {
3158                        Slog.w(TAG, "Error bringing down backup stack");
3159                    }
3160                }
3161            } else {
3162                Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
3163            }
3164            tearDown(pkg);
3165        }
3166
3167        private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
3168            // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
3169            final String appSourceDir = pkg.applicationInfo.sourceDir;
3170            final String apkDir = new File(appSourceDir).getParent();
3171            FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
3172                    apkDir, appSourceDir, output);
3173
3174            // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM
3175            // doesn't have access to external storage.
3176
3177            // Save associated .obb content if it exists and we did save the apk
3178            // check for .obb and save those too
3179            final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_OWNER);
3180            final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0];
3181            if (obbDir != null) {
3182                if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
3183                File[] obbFiles = obbDir.listFiles();
3184                if (obbFiles != null) {
3185                    final String obbDirName = obbDir.getAbsolutePath();
3186                    for (File obb : obbFiles) {
3187                        FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
3188                                obbDirName, obb.getAbsolutePath(), output);
3189                    }
3190                }
3191            }
3192        }
3193
3194        private void finalizeBackup(OutputStream out) {
3195            try {
3196                // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes.
3197                byte[] eof = new byte[512 * 2]; // newly allocated == zero filled
3198                out.write(eof);
3199            } catch (IOException e) {
3200                Slog.w(TAG, "Error attempting to finalize backup stream");
3201            }
3202        }
3203
3204        private void writeAppManifest(PackageInfo pkg, File manifestFile,
3205                boolean withApk, boolean withWidgets) throws IOException {
3206            // Manifest format. All data are strings ending in LF:
3207            //     BACKUP_MANIFEST_VERSION, currently 1
3208            //
3209            // Version 1:
3210            //     package name
3211            //     package's versionCode
3212            //     platform versionCode
3213            //     getInstallerPackageName() for this package (maybe empty)
3214            //     boolean: "1" if archive includes .apk; any other string means not
3215            //     number of signatures == N
3216            // N*:    signature byte array in ascii format per Signature.toCharsString()
3217            StringBuilder builder = new StringBuilder(4096);
3218            StringBuilderPrinter printer = new StringBuilderPrinter(builder);
3219
3220            printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
3221            printer.println(pkg.packageName);
3222            printer.println(Integer.toString(pkg.versionCode));
3223            printer.println(Integer.toString(Build.VERSION.SDK_INT));
3224
3225            String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
3226            printer.println((installerName != null) ? installerName : "");
3227
3228            printer.println(withApk ? "1" : "0");
3229            if (pkg.signatures == null) {
3230                printer.println("0");
3231            } else {
3232                printer.println(Integer.toString(pkg.signatures.length));
3233                for (Signature sig : pkg.signatures) {
3234                    printer.println(sig.toCharsString());
3235                }
3236            }
3237
3238            FileOutputStream outstream = new FileOutputStream(manifestFile);
3239            outstream.write(builder.toString().getBytes());
3240            outstream.close();
3241        }
3242
3243        // Widget metadata format. All header entries are strings ending in LF:
3244        //
3245        // Version 1 header:
3246        //     BACKUP_METADATA_VERSION, currently "1"
3247        //     package name
3248        //
3249        // File data (all integers are binary in network byte order)
3250        // *N: 4 : integer token identifying which metadata blob
3251        //     4 : integer size of this blob = N
3252        //     N : raw bytes of this metadata blob
3253        //
3254        // Currently understood blobs (always in network byte order):
3255        //
3256        //     widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
3257        //
3258        // Unrecognized blobs are *ignored*, not errors.
3259        private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
3260                throws IOException {
3261            StringBuilder b = new StringBuilder(512);
3262            StringBuilderPrinter printer = new StringBuilderPrinter(b);
3263            printer.println(Integer.toString(BACKUP_METADATA_VERSION));
3264            printer.println(pkg.packageName);
3265
3266            FileOutputStream fout = new FileOutputStream(destination);
3267            BufferedOutputStream bout = new BufferedOutputStream(fout);
3268            DataOutputStream out = new DataOutputStream(bout);
3269            bout.write(b.toString().getBytes());    // bypassing DataOutputStream
3270
3271            if (widgetData != null && widgetData.length > 0) {
3272                out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
3273                out.writeInt(widgetData.length);
3274                out.write(widgetData);
3275            }
3276            bout.flush();
3277            out.close();
3278        }
3279
3280        private void tearDown(PackageInfo pkg) {
3281            if (pkg != null) {
3282                final ApplicationInfo app = pkg.applicationInfo;
3283                if (app != null) {
3284                    try {
3285                        // unbind and tidy up even on timeout or failure, just in case
3286                        mActivityManager.unbindBackupAgent(app);
3287
3288                        // The agent was running with a stub Application object, so shut it down.
3289                        if (app.uid != Process.SYSTEM_UID
3290                                && app.uid != Process.PHONE_UID) {
3291                            if (MORE_DEBUG) Slog.d(TAG, "Backup complete, killing host process");
3292                            mActivityManager.killApplicationProcess(app.processName, app.uid);
3293                        } else {
3294                            if (MORE_DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
3295                        }
3296                    } catch (RemoteException e) {
3297                        Slog.d(TAG, "Lost app trying to shut down");
3298                    }
3299                }
3300            }
3301        }
3302
3303        // wrappers for observer use
3304        void sendStartBackup() {
3305            if (mObserver != null) {
3306                try {
3307                    mObserver.onStartBackup();
3308                } catch (RemoteException e) {
3309                    Slog.w(TAG, "full backup observer went away: startBackup");
3310                    mObserver = null;
3311                }
3312            }
3313        }
3314
3315        void sendOnBackupPackage(String name) {
3316            if (mObserver != null) {
3317                try {
3318                    // TODO: use a more user-friendly name string
3319                    mObserver.onBackupPackage(name);
3320                } catch (RemoteException e) {
3321                    Slog.w(TAG, "full backup observer went away: backupPackage");
3322                    mObserver = null;
3323                }
3324            }
3325        }
3326
3327        void sendEndBackup() {
3328            if (mObserver != null) {
3329                try {
3330                    mObserver.onEndBackup();
3331                } catch (RemoteException e) {
3332                    Slog.w(TAG, "full backup observer went away: endBackup");
3333                    mObserver = null;
3334                }
3335            }
3336        }
3337    }
3338
3339
3340    // ----- Full restore from a file/socket -----
3341
3342    // Description of a file in the restore datastream
3343    static class FileMetadata {
3344        String packageName;             // name of the owning app
3345        String installerPackageName;    // name of the market-type app that installed the owner
3346        int type;                       // e.g. BackupAgent.TYPE_DIRECTORY
3347        String domain;                  // e.g. FullBackup.DATABASE_TREE_TOKEN
3348        String path;                    // subpath within the semantic domain
3349        long mode;                      // e.g. 0666 (actually int)
3350        long mtime;                     // last mod time, UTC time_t (actually int)
3351        long size;                      // bytes of content
3352
3353        @Override
3354        public String toString() {
3355            StringBuilder sb = new StringBuilder(128);
3356            sb.append("FileMetadata{");
3357            sb.append(packageName); sb.append(',');
3358            sb.append(type); sb.append(',');
3359            sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
3360            sb.append(size);
3361            sb.append('}');
3362            return sb.toString();
3363        }
3364    }
3365
3366    enum RestorePolicy {
3367        IGNORE,
3368        ACCEPT,
3369        ACCEPT_IF_APK
3370    }
3371
3372    class PerformFullRestoreTask extends ObbServiceClient implements Runnable {
3373        ParcelFileDescriptor mInputFile;
3374        String mCurrentPassword;
3375        String mDecryptPassword;
3376        IFullBackupRestoreObserver mObserver;
3377        AtomicBoolean mLatchObject;
3378        IBackupAgent mAgent;
3379        String mAgentPackage;
3380        ApplicationInfo mTargetApp;
3381        FullBackupObbConnection mObbConnection = null;
3382        ParcelFileDescriptor[] mPipes = null;
3383        byte[] mWidgetData = null;
3384
3385        long mBytes;
3386
3387        // possible handling states for a given package in the restore dataset
3388        final HashMap<String, RestorePolicy> mPackagePolicies
3389                = new HashMap<String, RestorePolicy>();
3390
3391        // installer package names for each encountered app, derived from the manifests
3392        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
3393
3394        // Signatures for a given package found in its manifest file
3395        final HashMap<String, Signature[]> mManifestSignatures
3396                = new HashMap<String, Signature[]>();
3397
3398        // Packages we've already wiped data on when restoring their first file
3399        final HashSet<String> mClearedPackages = new HashSet<String>();
3400
3401        PerformFullRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
3402                IFullBackupRestoreObserver observer, AtomicBoolean latch) {
3403            mInputFile = fd;
3404            mCurrentPassword = curPassword;
3405            mDecryptPassword = decryptPassword;
3406            mObserver = observer;
3407            mLatchObject = latch;
3408            mAgent = null;
3409            mAgentPackage = null;
3410            mTargetApp = null;
3411            mObbConnection = new FullBackupObbConnection();
3412
3413            // Which packages we've already wiped data on.  We prepopulate this
3414            // with a whitelist of packages known to be unclearable.
3415            mClearedPackages.add("android");
3416            mClearedPackages.add("com.android.providers.settings");
3417
3418        }
3419
3420        class RestoreFileRunnable implements Runnable {
3421            IBackupAgent mAgent;
3422            FileMetadata mInfo;
3423            ParcelFileDescriptor mSocket;
3424            int mToken;
3425
3426            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
3427                    ParcelFileDescriptor socket, int token) throws IOException {
3428                mAgent = agent;
3429                mInfo = info;
3430                mToken = token;
3431
3432                // This class is used strictly for process-local binder invocations.  The
3433                // semantics of ParcelFileDescriptor differ in this case; in particular, we
3434                // do not automatically get a 'dup'ed descriptor that we can can continue
3435                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
3436                // before proceeding to do the restore.
3437                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
3438            }
3439
3440            @Override
3441            public void run() {
3442                try {
3443                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
3444                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
3445                            mToken, mBackupManagerBinder);
3446                } catch (RemoteException e) {
3447                    // never happens; this is used strictly for local binder calls
3448                }
3449            }
3450        }
3451
3452        @Override
3453        public void run() {
3454            Slog.i(TAG, "--- Performing full-dataset restore ---");
3455            mObbConnection.establish();
3456            sendStartRestore();
3457
3458            // Are we able to restore shared-storage data?
3459            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
3460                mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
3461            }
3462
3463            FileInputStream rawInStream = null;
3464            DataInputStream rawDataIn = null;
3465            try {
3466                if (!backupPasswordMatches(mCurrentPassword)) {
3467                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
3468                    return;
3469                }
3470
3471                mBytes = 0;
3472                byte[] buffer = new byte[32 * 1024];
3473                rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
3474                rawDataIn = new DataInputStream(rawInStream);
3475
3476                // First, parse out the unencrypted/uncompressed header
3477                boolean compressed = false;
3478                InputStream preCompressStream = rawInStream;
3479                final InputStream in;
3480
3481                boolean okay = false;
3482                final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
3483                byte[] streamHeader = new byte[headerLen];
3484                rawDataIn.readFully(streamHeader);
3485                byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
3486                if (Arrays.equals(magicBytes, streamHeader)) {
3487                    // okay, header looks good.  now parse out the rest of the fields.
3488                    String s = readHeaderLine(rawInStream);
3489                    final int archiveVersion = Integer.parseInt(s);
3490                    if (archiveVersion <= BACKUP_FILE_VERSION) {
3491                        // okay, it's a version we recognize.  if it's version 1, we may need
3492                        // to try two different PBKDF2 regimes to compare checksums.
3493                        final boolean pbkdf2Fallback = (archiveVersion == 1);
3494
3495                        s = readHeaderLine(rawInStream);
3496                        compressed = (Integer.parseInt(s) != 0);
3497                        s = readHeaderLine(rawInStream);
3498                        if (s.equals("none")) {
3499                            // no more header to parse; we're good to go
3500                            okay = true;
3501                        } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
3502                            preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
3503                                    rawInStream);
3504                            if (preCompressStream != null) {
3505                                okay = true;
3506                            }
3507                        } else Slog.w(TAG, "Archive is encrypted but no password given");
3508                    } else Slog.w(TAG, "Wrong header version: " + s);
3509                } else Slog.w(TAG, "Didn't read the right header magic");
3510
3511                if (!okay) {
3512                    Slog.w(TAG, "Invalid restore data; aborting.");
3513                    return;
3514                }
3515
3516                // okay, use the right stream layer based on compression
3517                in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
3518
3519                boolean didRestore;
3520                do {
3521                    didRestore = restoreOneFile(in, buffer);
3522                } while (didRestore);
3523
3524                if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
3525            } catch (IOException e) {
3526                Slog.e(TAG, "Unable to read restore input");
3527            } finally {
3528                tearDownPipes();
3529                tearDownAgent(mTargetApp);
3530
3531                try {
3532                    if (rawDataIn != null) rawDataIn.close();
3533                    if (rawInStream != null) rawInStream.close();
3534                    mInputFile.close();
3535                } catch (IOException e) {
3536                    Slog.w(TAG, "Close of restore data pipe threw", e);
3537                    /* nothing we can do about this */
3538                }
3539                synchronized (mCurrentOpLock) {
3540                    mCurrentOperations.clear();
3541                }
3542                synchronized (mLatchObject) {
3543                    mLatchObject.set(true);
3544                    mLatchObject.notifyAll();
3545                }
3546                mObbConnection.tearDown();
3547                sendEndRestore();
3548                Slog.d(TAG, "Full restore pass complete.");
3549                mWakelock.release();
3550            }
3551        }
3552
3553        String readHeaderLine(InputStream in) throws IOException {
3554            int c;
3555            StringBuilder buffer = new StringBuilder(80);
3556            while ((c = in.read()) >= 0) {
3557                if (c == '\n') break;   // consume and discard the newlines
3558                buffer.append((char)c);
3559            }
3560            return buffer.toString();
3561        }
3562
3563        InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
3564                int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
3565                boolean doLog) {
3566            InputStream result = null;
3567
3568            try {
3569                Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
3570                SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt,
3571                        rounds);
3572                byte[] IV = hexToByteArray(userIvHex);
3573                IvParameterSpec ivSpec = new IvParameterSpec(IV);
3574                c.init(Cipher.DECRYPT_MODE,
3575                        new SecretKeySpec(userKey.getEncoded(), "AES"),
3576                        ivSpec);
3577                byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
3578                byte[] mkBlob = c.doFinal(mkCipher);
3579
3580                // first, the master key IV
3581                int offset = 0;
3582                int len = mkBlob[offset++];
3583                IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
3584                offset += len;
3585                // then the master key itself
3586                len = mkBlob[offset++];
3587                byte[] mk = Arrays.copyOfRange(mkBlob,
3588                        offset, offset + len);
3589                offset += len;
3590                // and finally the master key checksum hash
3591                len = mkBlob[offset++];
3592                byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
3593                        offset, offset + len);
3594
3595                // now validate the decrypted master key against the checksum
3596                byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds);
3597                if (Arrays.equals(calculatedCk, mkChecksum)) {
3598                    ivSpec = new IvParameterSpec(IV);
3599                    c.init(Cipher.DECRYPT_MODE,
3600                            new SecretKeySpec(mk, "AES"),
3601                            ivSpec);
3602                    // Only if all of the above worked properly will 'result' be assigned
3603                    result = new CipherInputStream(rawInStream, c);
3604                } else if (doLog) Slog.w(TAG, "Incorrect password");
3605            } catch (InvalidAlgorithmParameterException e) {
3606                if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e);
3607            } catch (BadPaddingException e) {
3608                // This case frequently occurs when the wrong password is used to decrypt
3609                // the master key.  Use the identical "incorrect password" log text as is
3610                // used in the checksum failure log in order to avoid providing additional
3611                // information to an attacker.
3612                if (doLog) Slog.w(TAG, "Incorrect password");
3613            } catch (IllegalBlockSizeException e) {
3614                if (doLog) Slog.w(TAG, "Invalid block size in master key");
3615            } catch (NoSuchAlgorithmException e) {
3616                if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!");
3617            } catch (NoSuchPaddingException e) {
3618                if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!");
3619            } catch (InvalidKeyException e) {
3620                if (doLog) Slog.w(TAG, "Illegal password; aborting");
3621            }
3622
3623            return result;
3624        }
3625
3626        InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
3627                InputStream rawInStream) {
3628            InputStream result = null;
3629            try {
3630                if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
3631
3632                    String userSaltHex = readHeaderLine(rawInStream); // 5
3633                    byte[] userSalt = hexToByteArray(userSaltHex);
3634
3635                    String ckSaltHex = readHeaderLine(rawInStream); // 6
3636                    byte[] ckSalt = hexToByteArray(ckSaltHex);
3637
3638                    int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
3639                    String userIvHex = readHeaderLine(rawInStream); // 8
3640
3641                    String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
3642
3643                    // decrypt the master key blob
3644                    result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt,
3645                            rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
3646                    if (result == null && pbkdf2Fallback) {
3647                        result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt,
3648                                rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
3649                    }
3650                } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
3651            } catch (NumberFormatException e) {
3652                Slog.w(TAG, "Can't parse restore data header");
3653            } catch (IOException e) {
3654                Slog.w(TAG, "Can't read input header");
3655            }
3656
3657            return result;
3658        }
3659
3660        boolean restoreOneFile(InputStream instream, byte[] buffer) {
3661            FileMetadata info;
3662            try {
3663                info = readTarHeaders(instream);
3664                if (info != null) {
3665                    if (MORE_DEBUG) {
3666                        dumpFileMetadata(info);
3667                    }
3668
3669                    final String pkg = info.packageName;
3670                    if (!pkg.equals(mAgentPackage)) {
3671                        // okay, change in package; set up our various
3672                        // bookkeeping if we haven't seen it yet
3673                        if (!mPackagePolicies.containsKey(pkg)) {
3674                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3675                        }
3676
3677                        // Clean up the previous agent relationship if necessary,
3678                        // and let the observer know we're considering a new app.
3679                        if (mAgent != null) {
3680                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
3681                            // Now we're really done
3682                            tearDownPipes();
3683                            tearDownAgent(mTargetApp);
3684                            mTargetApp = null;
3685                            mAgentPackage = null;
3686                        }
3687                    }
3688
3689                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
3690                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
3691                        mPackageInstallers.put(pkg, info.installerPackageName);
3692                        // We've read only the manifest content itself at this point,
3693                        // so consume the footer before looping around to the next
3694                        // input file
3695                        skipTarPadding(info.size, instream);
3696                        sendOnRestorePackage(pkg);
3697                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
3698                        // Metadata blobs!
3699                        readMetadata(info, instream);
3700                    } else {
3701                        // Non-manifest, so it's actual file data.  Is this a package
3702                        // we're ignoring?
3703                        boolean okay = true;
3704                        RestorePolicy policy = mPackagePolicies.get(pkg);
3705                        switch (policy) {
3706                            case IGNORE:
3707                                okay = false;
3708                                break;
3709
3710                            case ACCEPT_IF_APK:
3711                                // If we're in accept-if-apk state, then the first file we
3712                                // see MUST be the apk.
3713                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
3714                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
3715                                    // Try to install the app.
3716                                    String installerName = mPackageInstallers.get(pkg);
3717                                    okay = installApk(info, installerName, instream);
3718                                    // good to go; promote to ACCEPT
3719                                    mPackagePolicies.put(pkg, (okay)
3720                                            ? RestorePolicy.ACCEPT
3721                                            : RestorePolicy.IGNORE);
3722                                    // At this point we've consumed this file entry
3723                                    // ourselves, so just strip the tar footer and
3724                                    // go on to the next file in the input stream
3725                                    skipTarPadding(info.size, instream);
3726                                    return true;
3727                                } else {
3728                                    // File data before (or without) the apk.  We can't
3729                                    // handle it coherently in this case so ignore it.
3730                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3731                                    okay = false;
3732                                }
3733                                break;
3734
3735                            case ACCEPT:
3736                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
3737                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
3738                                    // we can take the data without the apk, so we
3739                                    // *want* to do so.  skip the apk by declaring this
3740                                    // one file not-okay without changing the restore
3741                                    // policy for the package.
3742                                    okay = false;
3743                                }
3744                                break;
3745
3746                            default:
3747                                // Something has gone dreadfully wrong when determining
3748                                // the restore policy from the manifest.  Ignore the
3749                                // rest of this package's data.
3750                                Slog.e(TAG, "Invalid policy from manifest");
3751                                okay = false;
3752                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3753                                break;
3754                        }
3755
3756                        // If the policy is satisfied, go ahead and set up to pipe the
3757                        // data to the agent.
3758                        if (DEBUG && okay && mAgent != null) {
3759                            Slog.i(TAG, "Reusing existing agent instance");
3760                        }
3761                        if (okay && mAgent == null) {
3762                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
3763
3764                            try {
3765                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
3766
3767                                // If we haven't sent any data to this app yet, we probably
3768                                // need to clear it first.  Check that.
3769                                if (!mClearedPackages.contains(pkg)) {
3770                                    // apps with their own backup agents are
3771                                    // responsible for coherently managing a full
3772                                    // restore.
3773                                    if (mTargetApp.backupAgentName == null) {
3774                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
3775                                        clearApplicationDataSynchronous(pkg);
3776                                    } else {
3777                                        if (DEBUG) Slog.d(TAG, "backup agent ("
3778                                                + mTargetApp.backupAgentName + ") => no clear");
3779                                    }
3780                                    mClearedPackages.add(pkg);
3781                                } else {
3782                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
3783                                }
3784
3785                                // All set; now set up the IPC and launch the agent
3786                                setUpPipes();
3787                                mAgent = bindToAgentSynchronous(mTargetApp,
3788                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
3789                                mAgentPackage = pkg;
3790                            } catch (IOException e) {
3791                                // fall through to error handling
3792                            } catch (NameNotFoundException e) {
3793                                // fall through to error handling
3794                            }
3795
3796                            if (mAgent == null) {
3797                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
3798                                okay = false;
3799                                tearDownPipes();
3800                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3801                            }
3802                        }
3803
3804                        // Sanity check: make sure we never give data to the wrong app.  This
3805                        // should never happen but a little paranoia here won't go amiss.
3806                        if (okay && !pkg.equals(mAgentPackage)) {
3807                            Slog.e(TAG, "Restoring data for " + pkg
3808                                    + " but agent is for " + mAgentPackage);
3809                            okay = false;
3810                        }
3811
3812                        // At this point we have an agent ready to handle the full
3813                        // restore data as well as a pipe for sending data to
3814                        // that agent.  Tell the agent to start reading from the
3815                        // pipe.
3816                        if (okay) {
3817                            boolean agentSuccess = true;
3818                            long toCopy = info.size;
3819                            final int token = generateToken();
3820                            try {
3821                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
3822                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
3823                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
3824                                            + " : " + info.path);
3825                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
3826                                            info.size, info.type, info.path, info.mode,
3827                                            info.mtime, token, mBackupManagerBinder);
3828                                } else {
3829                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
3830                                            + info.path);
3831                                    // fire up the app's agent listening on the socket.  If
3832                                    // the agent is running in the system process we can't
3833                                    // just invoke it asynchronously, so we provide a thread
3834                                    // for it here.
3835                                    if (mTargetApp.processName.equals("system")) {
3836                                        Slog.d(TAG, "system process agent - spinning a thread");
3837                                        RestoreFileRunnable runner = new RestoreFileRunnable(
3838                                                mAgent, info, mPipes[0], token);
3839                                        new Thread(runner).start();
3840                                    } else {
3841                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
3842                                                info.domain, info.path, info.mode, info.mtime,
3843                                                token, mBackupManagerBinder);
3844                                    }
3845                                }
3846                            } catch (IOException e) {
3847                                // couldn't dup the socket for a process-local restore
3848                                Slog.d(TAG, "Couldn't establish restore");
3849                                agentSuccess = false;
3850                                okay = false;
3851                            } catch (RemoteException e) {
3852                                // whoops, remote entity went away.  We'll eat the content
3853                                // ourselves, then, and not copy it over.
3854                                Slog.e(TAG, "Agent crashed during full restore");
3855                                agentSuccess = false;
3856                                okay = false;
3857                            }
3858
3859                            // Copy over the data if the agent is still good
3860                            if (okay) {
3861                                boolean pipeOkay = true;
3862                                FileOutputStream pipe = new FileOutputStream(
3863                                        mPipes[1].getFileDescriptor());
3864                                while (toCopy > 0) {
3865                                    int toRead = (toCopy > buffer.length)
3866                                    ? buffer.length : (int)toCopy;
3867                                    int nRead = instream.read(buffer, 0, toRead);
3868                                    if (nRead >= 0) mBytes += nRead;
3869                                    if (nRead <= 0) break;
3870                                    toCopy -= nRead;
3871
3872                                    // send it to the output pipe as long as things
3873                                    // are still good
3874                                    if (pipeOkay) {
3875                                        try {
3876                                            pipe.write(buffer, 0, nRead);
3877                                        } catch (IOException e) {
3878                                            Slog.e(TAG, "Failed to write to restore pipe", e);
3879                                            pipeOkay = false;
3880                                        }
3881                                    }
3882                                }
3883
3884                                // done sending that file!  Now we just need to consume
3885                                // the delta from info.size to the end of block.
3886                                skipTarPadding(info.size, instream);
3887
3888                                // and now that we've sent it all, wait for the remote
3889                                // side to acknowledge receipt
3890                                agentSuccess = waitUntilOperationComplete(token);
3891                            }
3892
3893                            // okay, if the remote end failed at any point, deal with
3894                            // it by ignoring the rest of the restore on it
3895                            if (!agentSuccess) {
3896                                mBackupHandler.removeMessages(MSG_TIMEOUT);
3897                                tearDownPipes();
3898                                tearDownAgent(mTargetApp);
3899                                mAgent = null;
3900                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3901                            }
3902                        }
3903
3904                        // Problems setting up the agent communication, or an already-
3905                        // ignored package: skip to the next tar stream entry by
3906                        // reading and discarding this file.
3907                        if (!okay) {
3908                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
3909                            long bytesToConsume = (info.size + 511) & ~511;
3910                            while (bytesToConsume > 0) {
3911                                int toRead = (bytesToConsume > buffer.length)
3912                                ? buffer.length : (int)bytesToConsume;
3913                                long nRead = instream.read(buffer, 0, toRead);
3914                                if (nRead >= 0) mBytes += nRead;
3915                                if (nRead <= 0) break;
3916                                bytesToConsume -= nRead;
3917                            }
3918                        }
3919                    }
3920                }
3921            } catch (IOException e) {
3922                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
3923                // treat as EOF
3924                info = null;
3925            }
3926
3927            return (info != null);
3928        }
3929
3930        void setUpPipes() throws IOException {
3931            mPipes = ParcelFileDescriptor.createPipe();
3932        }
3933
3934        void tearDownPipes() {
3935            if (mPipes != null) {
3936                try {
3937                    mPipes[0].close();
3938                    mPipes[0] = null;
3939                    mPipes[1].close();
3940                    mPipes[1] = null;
3941                } catch (IOException e) {
3942                    Slog.w(TAG, "Couldn't close agent pipes", e);
3943                }
3944                mPipes = null;
3945            }
3946        }
3947
3948        void tearDownAgent(ApplicationInfo app) {
3949            if (mAgent != null) {
3950                try {
3951                    // unbind and tidy up even on timeout or failure, just in case
3952                    mActivityManager.unbindBackupAgent(app);
3953
3954                    // The agent was running with a stub Application object, so shut it down.
3955                    // !!! We hardcode the confirmation UI's package name here rather than use a
3956                    //     manifest flag!  TODO something less direct.
3957                    if (app.uid != Process.SYSTEM_UID
3958                            && !app.packageName.equals("com.android.backupconfirm")) {
3959                        if (DEBUG) Slog.d(TAG, "Killing host process");
3960                        mActivityManager.killApplicationProcess(app.processName, app.uid);
3961                    } else {
3962                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
3963                    }
3964                } catch (RemoteException e) {
3965                    Slog.d(TAG, "Lost app trying to shut down");
3966                }
3967                mAgent = null;
3968            }
3969        }
3970
3971        class RestoreInstallObserver extends IPackageInstallObserver.Stub {
3972            final AtomicBoolean mDone = new AtomicBoolean();
3973            String mPackageName;
3974            int mResult;
3975
3976            public void reset() {
3977                synchronized (mDone) {
3978                    mDone.set(false);
3979                }
3980            }
3981
3982            public void waitForCompletion() {
3983                synchronized (mDone) {
3984                    while (mDone.get() == false) {
3985                        try {
3986                            mDone.wait();
3987                        } catch (InterruptedException e) { }
3988                    }
3989                }
3990            }
3991
3992            int getResult() {
3993                return mResult;
3994            }
3995
3996            @Override
3997            public void packageInstalled(String packageName, int returnCode)
3998                    throws RemoteException {
3999                synchronized (mDone) {
4000                    mResult = returnCode;
4001                    mPackageName = packageName;
4002                    mDone.set(true);
4003                    mDone.notifyAll();
4004                }
4005            }
4006        }
4007
4008        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
4009            final AtomicBoolean mDone = new AtomicBoolean();
4010            int mResult;
4011
4012            public void reset() {
4013                synchronized (mDone) {
4014                    mDone.set(false);
4015                }
4016            }
4017
4018            public void waitForCompletion() {
4019                synchronized (mDone) {
4020                    while (mDone.get() == false) {
4021                        try {
4022                            mDone.wait();
4023                        } catch (InterruptedException e) { }
4024                    }
4025                }
4026            }
4027
4028            @Override
4029            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
4030                synchronized (mDone) {
4031                    mResult = returnCode;
4032                    mDone.set(true);
4033                    mDone.notifyAll();
4034                }
4035            }
4036        }
4037
4038        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
4039        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
4040
4041        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
4042            boolean okay = true;
4043
4044            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
4045
4046            // The file content is an .apk file.  Copy it out to a staging location and
4047            // attempt to install it.
4048            File apkFile = new File(mDataDir, info.packageName);
4049            try {
4050                FileOutputStream apkStream = new FileOutputStream(apkFile);
4051                byte[] buffer = new byte[32 * 1024];
4052                long size = info.size;
4053                while (size > 0) {
4054                    long toRead = (buffer.length < size) ? buffer.length : size;
4055                    int didRead = instream.read(buffer, 0, (int)toRead);
4056                    if (didRead >= 0) mBytes += didRead;
4057                    apkStream.write(buffer, 0, didRead);
4058                    size -= didRead;
4059                }
4060                apkStream.close();
4061
4062                // make sure the installer can read it
4063                apkFile.setReadable(true, false);
4064
4065                // Now install it
4066                Uri packageUri = Uri.fromFile(apkFile);
4067                mInstallObserver.reset();
4068                mPackageManager.installPackage(packageUri, mInstallObserver,
4069                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
4070                        installerPackage);
4071                mInstallObserver.waitForCompletion();
4072
4073                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
4074                    // The only time we continue to accept install of data even if the
4075                    // apk install failed is if we had already determined that we could
4076                    // accept the data regardless.
4077                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
4078                        okay = false;
4079                    }
4080                } else {
4081                    // Okay, the install succeeded.  Make sure it was the right app.
4082                    boolean uninstall = false;
4083                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
4084                        Slog.w(TAG, "Restore stream claimed to include apk for "
4085                                + info.packageName + " but apk was really "
4086                                + mInstallObserver.mPackageName);
4087                        // delete the package we just put in place; it might be fraudulent
4088                        okay = false;
4089                        uninstall = true;
4090                    } else {
4091                        try {
4092                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
4093                                    PackageManager.GET_SIGNATURES);
4094                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
4095                                Slog.w(TAG, "Restore stream contains apk of package "
4096                                        + info.packageName + " but it disallows backup/restore");
4097                                okay = false;
4098                            } else {
4099                                // So far so good -- do the signatures match the manifest?
4100                                Signature[] sigs = mManifestSignatures.get(info.packageName);
4101                                if (signaturesMatch(sigs, pkg)) {
4102                                    // If this is a system-uid app without a declared backup agent,
4103                                    // don't restore any of the file data.
4104                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
4105                                            && (pkg.applicationInfo.backupAgentName == null)) {
4106                                        Slog.w(TAG, "Installed app " + info.packageName
4107                                                + " has restricted uid and no agent");
4108                                        okay = false;
4109                                    }
4110                                } else {
4111                                    Slog.w(TAG, "Installed app " + info.packageName
4112                                            + " signatures do not match restore manifest");
4113                                    okay = false;
4114                                    uninstall = true;
4115                                }
4116                            }
4117                        } catch (NameNotFoundException e) {
4118                            Slog.w(TAG, "Install of package " + info.packageName
4119                                    + " succeeded but now not found");
4120                            okay = false;
4121                        }
4122                    }
4123
4124                    // If we're not okay at this point, we need to delete the package
4125                    // that we just installed.
4126                    if (uninstall) {
4127                        mDeleteObserver.reset();
4128                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
4129                                mDeleteObserver, 0);
4130                        mDeleteObserver.waitForCompletion();
4131                    }
4132                }
4133            } catch (IOException e) {
4134                Slog.e(TAG, "Unable to transcribe restored apk for install");
4135                okay = false;
4136            } finally {
4137                apkFile.delete();
4138            }
4139
4140            return okay;
4141        }
4142
4143        // Given an actual file content size, consume the post-content padding mandated
4144        // by the tar format.
4145        void skipTarPadding(long size, InputStream instream) throws IOException {
4146            long partial = (size + 512) % 512;
4147            if (partial > 0) {
4148                final int needed = 512 - (int)partial;
4149                byte[] buffer = new byte[needed];
4150                if (readExactly(instream, buffer, 0, needed) == needed) {
4151                    mBytes += needed;
4152                } else throw new IOException("Unexpected EOF in padding");
4153            }
4154        }
4155
4156        // Read a widget metadata file, returning the restored blob
4157        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
4158            byte[] data = null;
4159
4160            // Fail on suspiciously large widget dump files
4161            if (info.size > 64 * 1024) {
4162                throw new IOException("Metadata too big; corrupt? size=" + info.size);
4163            }
4164
4165            byte[] buffer = new byte[(int) info.size];
4166            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
4167                mBytes += info.size;
4168            } else throw new IOException("Unexpected EOF in widget data");
4169
4170            String[] str = new String[1];
4171            int offset = extractLine(buffer, 0, str);
4172            int version = Integer.parseInt(str[0]);
4173            if (version == BACKUP_MANIFEST_VERSION) {
4174                offset = extractLine(buffer, offset, str);
4175                final String pkg = str[0];
4176                if (info.packageName.equals(pkg)) {
4177                    // Data checks out -- the rest of the buffer is a concatenation of
4178                    // binary blobs as described in the comment at writeAppWidgetData()
4179                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
4180                            offset, buffer.length - offset);
4181                    DataInputStream in = new DataInputStream(bin);
4182                    while (bin.available() > 0) {
4183                        int token = in.readInt();
4184                        int size = in.readInt();
4185                        if (size > 64 * 1024) {
4186                            throw new IOException("Datum "
4187                                    + Integer.toHexString(token)
4188                                    + " too big; corrupt? size=" + info.size);
4189                        }
4190                        switch (token) {
4191                            case BACKUP_WIDGET_METADATA_TOKEN:
4192                            {
4193                                if (MORE_DEBUG) {
4194                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
4195                                }
4196                                mWidgetData = new byte[size];
4197                                in.read(mWidgetData);
4198                                break;
4199                            }
4200                            default:
4201                            {
4202                                if (DEBUG) {
4203                                    Slog.i(TAG, "Ignoring metadata blob "
4204                                            + Integer.toHexString(token)
4205                                            + " for " + info.packageName);
4206                                }
4207                                in.skipBytes(size);
4208                                break;
4209                            }
4210                        }
4211                    }
4212                } else {
4213                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
4214                            + " but widget data for " + pkg);
4215                }
4216            } else {
4217                Slog.w(TAG, "Unsupported metadata version " + version);
4218            }
4219        }
4220
4221        // Returns a policy constant; takes a buffer arg to reduce memory churn
4222        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
4223                throws IOException {
4224            // Fail on suspiciously large manifest files
4225            if (info.size > 64 * 1024) {
4226                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
4227            }
4228
4229            byte[] buffer = new byte[(int) info.size];
4230            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
4231                mBytes += info.size;
4232            } else throw new IOException("Unexpected EOF in manifest");
4233
4234            RestorePolicy policy = RestorePolicy.IGNORE;
4235            String[] str = new String[1];
4236            int offset = 0;
4237
4238            try {
4239                offset = extractLine(buffer, offset, str);
4240                int version = Integer.parseInt(str[0]);
4241                if (version == BACKUP_MANIFEST_VERSION) {
4242                    offset = extractLine(buffer, offset, str);
4243                    String manifestPackage = str[0];
4244                    // TODO: handle <original-package>
4245                    if (manifestPackage.equals(info.packageName)) {
4246                        offset = extractLine(buffer, offset, str);
4247                        version = Integer.parseInt(str[0]);  // app version
4248                        offset = extractLine(buffer, offset, str);
4249                        int platformVersion = Integer.parseInt(str[0]);
4250                        offset = extractLine(buffer, offset, str);
4251                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
4252                        offset = extractLine(buffer, offset, str);
4253                        boolean hasApk = str[0].equals("1");
4254                        offset = extractLine(buffer, offset, str);
4255                        int numSigs = Integer.parseInt(str[0]);
4256                        if (numSigs > 0) {
4257                            Signature[] sigs = new Signature[numSigs];
4258                            for (int i = 0; i < numSigs; i++) {
4259                                offset = extractLine(buffer, offset, str);
4260                                sigs[i] = new Signature(str[0]);
4261                            }
4262                            mManifestSignatures.put(info.packageName, sigs);
4263
4264                            // Okay, got the manifest info we need...
4265                            try {
4266                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
4267                                        info.packageName, PackageManager.GET_SIGNATURES);
4268                                // Fall through to IGNORE if the app explicitly disallows backup
4269                                final int flags = pkgInfo.applicationInfo.flags;
4270                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
4271                                    // Restore system-uid-space packages only if they have
4272                                    // defined a custom backup agent
4273                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
4274                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
4275                                        // Verify signatures against any installed version; if they
4276                                        // don't match, then we fall though and ignore the data.  The
4277                                        // signatureMatch() method explicitly ignores the signature
4278                                        // check for packages installed on the system partition, because
4279                                        // such packages are signed with the platform cert instead of
4280                                        // the app developer's cert, so they're different on every
4281                                        // device.
4282                                        if (signaturesMatch(sigs, pkgInfo)) {
4283                                            if (pkgInfo.versionCode >= version) {
4284                                                Slog.i(TAG, "Sig + version match; taking data");
4285                                                policy = RestorePolicy.ACCEPT;
4286                                            } else {
4287                                                // The data is from a newer version of the app than
4288                                                // is presently installed.  That means we can only
4289                                                // use it if the matching apk is also supplied.
4290                                                Slog.d(TAG, "Data version " + version
4291                                                        + " is newer than installed version "
4292                                                        + pkgInfo.versionCode + " - requiring apk");
4293                                                policy = RestorePolicy.ACCEPT_IF_APK;
4294                                            }
4295                                        } else {
4296                                            Slog.w(TAG, "Restore manifest signatures do not match "
4297                                                    + "installed application for " + info.packageName);
4298                                        }
4299                                    } else {
4300                                        Slog.w(TAG, "Package " + info.packageName
4301                                                + " is system level with no agent");
4302                                    }
4303                                } else {
4304                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
4305                                            + info.packageName + " but allowBackup=false");
4306                                }
4307                            } catch (NameNotFoundException e) {
4308                                // Okay, the target app isn't installed.  We can process
4309                                // the restore properly only if the dataset provides the
4310                                // apk file and we can successfully install it.
4311                                if (DEBUG) Slog.i(TAG, "Package " + info.packageName
4312                                        + " not installed; requiring apk in dataset");
4313                                policy = RestorePolicy.ACCEPT_IF_APK;
4314                            }
4315
4316                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
4317                                Slog.i(TAG, "Cannot restore package " + info.packageName
4318                                        + " without the matching .apk");
4319                            }
4320                        } else {
4321                            Slog.i(TAG, "Missing signature on backed-up package "
4322                                    + info.packageName);
4323                        }
4324                    } else {
4325                        Slog.i(TAG, "Expected package " + info.packageName
4326                                + " but restore manifest claims " + manifestPackage);
4327                    }
4328                } else {
4329                    Slog.i(TAG, "Unknown restore manifest version " + version
4330                            + " for package " + info.packageName);
4331                }
4332            } catch (NumberFormatException e) {
4333                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
4334            } catch (IllegalArgumentException e) {
4335                Slog.w(TAG, e.getMessage());
4336            }
4337
4338            return policy;
4339        }
4340
4341        // Builds a line from a byte buffer starting at 'offset', and returns
4342        // the index of the next unconsumed data in the buffer.
4343        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
4344            final int end = buffer.length;
4345            if (offset >= end) throw new IOException("Incomplete data");
4346
4347            int pos;
4348            for (pos = offset; pos < end; pos++) {
4349                byte c = buffer[pos];
4350                // at LF we declare end of line, and return the next char as the
4351                // starting point for the next time through
4352                if (c == '\n') {
4353                    break;
4354                }
4355            }
4356            outStr[0] = new String(buffer, offset, pos - offset);
4357            pos++;  // may be pointing an extra byte past the end but that's okay
4358            return pos;
4359        }
4360
4361        void dumpFileMetadata(FileMetadata info) {
4362            if (DEBUG) {
4363                StringBuilder b = new StringBuilder(128);
4364
4365                // mode string
4366                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
4367                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
4368                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
4369                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
4370                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
4371                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
4372                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
4373                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
4374                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
4375                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
4376                b.append(String.format(" %9d ", info.size));
4377
4378                Date stamp = new Date(info.mtime);
4379                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
4380
4381                b.append(info.packageName);
4382                b.append(" :: ");
4383                b.append(info.domain);
4384                b.append(" :: ");
4385                b.append(info.path);
4386
4387                Slog.i(TAG, b.toString());
4388            }
4389        }
4390        // Consume a tar file header block [sequence] and accumulate the relevant metadata
4391        FileMetadata readTarHeaders(InputStream instream) throws IOException {
4392            byte[] block = new byte[512];
4393            FileMetadata info = null;
4394
4395            boolean gotHeader = readTarHeader(instream, block);
4396            if (gotHeader) {
4397                try {
4398                    // okay, presume we're okay, and extract the various metadata
4399                    info = new FileMetadata();
4400                    info.size = extractRadix(block, 124, 12, 8);
4401                    info.mtime = extractRadix(block, 136, 12, 8);
4402                    info.mode = extractRadix(block, 100, 8, 8);
4403
4404                    info.path = extractString(block, 345, 155); // prefix
4405                    String path = extractString(block, 0, 100);
4406                    if (path.length() > 0) {
4407                        if (info.path.length() > 0) info.path += '/';
4408                        info.path += path;
4409                    }
4410
4411                    // tar link indicator field: 1 byte at offset 156 in the header.
4412                    int typeChar = block[156];
4413                    if (typeChar == 'x') {
4414                        // pax extended header, so we need to read that
4415                        gotHeader = readPaxExtendedHeader(instream, info);
4416                        if (gotHeader) {
4417                            // and after a pax extended header comes another real header -- read
4418                            // that to find the real file type
4419                            gotHeader = readTarHeader(instream, block);
4420                        }
4421                        if (!gotHeader) throw new IOException("Bad or missing pax header");
4422
4423                        typeChar = block[156];
4424                    }
4425
4426                    switch (typeChar) {
4427                        case '0': info.type = BackupAgent.TYPE_FILE; break;
4428                        case '5': {
4429                            info.type = BackupAgent.TYPE_DIRECTORY;
4430                            if (info.size != 0) {
4431                                Slog.w(TAG, "Directory entry with nonzero size in header");
4432                                info.size = 0;
4433                            }
4434                            break;
4435                        }
4436                        case 0: {
4437                            // presume EOF
4438                            if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
4439                            return null;
4440                        }
4441                        default: {
4442                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
4443                            throw new IOException("Unknown entity type " + typeChar);
4444                        }
4445                    }
4446
4447                    // Parse out the path
4448                    //
4449                    // first: apps/shared/unrecognized
4450                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
4451                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
4452                        // File in shared storage.  !!! TODO: implement this.
4453                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
4454                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
4455                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
4456                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
4457                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
4458                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
4459                        // App content!  Parse out the package name and domain
4460
4461                        // strip the apps/ prefix
4462                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
4463
4464                        // extract the package name
4465                        int slash = info.path.indexOf('/');
4466                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
4467                        info.packageName = info.path.substring(0, slash);
4468                        info.path = info.path.substring(slash+1);
4469
4470                        // if it's a manifest we're done, otherwise parse out the domains
4471                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) {
4472                            slash = info.path.indexOf('/');
4473                            if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
4474                            info.domain = info.path.substring(0, slash);
4475                            info.path = info.path.substring(slash + 1);
4476                        }
4477                    }
4478                } catch (IOException e) {
4479                    if (DEBUG) {
4480                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
4481                        HEXLOG(block);
4482                    }
4483                    throw e;
4484                }
4485            }
4486            return info;
4487        }
4488
4489        private void HEXLOG(byte[] block) {
4490            int offset = 0;
4491            int todo = block.length;
4492            StringBuilder buf = new StringBuilder(64);
4493            while (todo > 0) {
4494                buf.append(String.format("%04x   ", offset));
4495                int numThisLine = (todo > 16) ? 16 : todo;
4496                for (int i = 0; i < numThisLine; i++) {
4497                    buf.append(String.format("%02x ", block[offset+i]));
4498                }
4499                Slog.i("hexdump", buf.toString());
4500                buf.setLength(0);
4501                todo -= numThisLine;
4502                offset += numThisLine;
4503            }
4504        }
4505
4506        // Read exactly the given number of bytes into a buffer at the stated offset.
4507        // Returns false if EOF is encountered before the requested number of bytes
4508        // could be read.
4509        int readExactly(InputStream in, byte[] buffer, int offset, int size)
4510                throws IOException {
4511            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
4512
4513            int soFar = 0;
4514            while (soFar < size) {
4515                int nRead = in.read(buffer, offset + soFar, size - soFar);
4516                if (nRead <= 0) {
4517                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
4518                    break;
4519                }
4520                soFar += nRead;
4521            }
4522            return soFar;
4523        }
4524
4525        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
4526            final int got = readExactly(instream, block, 0, 512);
4527            if (got == 0) return false;     // Clean EOF
4528            if (got < 512) throw new IOException("Unable to read full block header");
4529            mBytes += 512;
4530            return true;
4531        }
4532
4533        // overwrites 'info' fields based on the pax extended header
4534        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
4535                throws IOException {
4536            // We should never see a pax extended header larger than this
4537            if (info.size > 32*1024) {
4538                Slog.w(TAG, "Suspiciously large pax header size " + info.size
4539                        + " - aborting");
4540                throw new IOException("Sanity failure: pax header size " + info.size);
4541            }
4542
4543            // read whole blocks, not just the content size
4544            int numBlocks = (int)((info.size + 511) >> 9);
4545            byte[] data = new byte[numBlocks * 512];
4546            if (readExactly(instream, data, 0, data.length) < data.length) {
4547                throw new IOException("Unable to read full pax header");
4548            }
4549            mBytes += data.length;
4550
4551            final int contentSize = (int) info.size;
4552            int offset = 0;
4553            do {
4554                // extract the line at 'offset'
4555                int eol = offset+1;
4556                while (eol < contentSize && data[eol] != ' ') eol++;
4557                if (eol >= contentSize) {
4558                    // error: we just hit EOD looking for the end of the size field
4559                    throw new IOException("Invalid pax data");
4560                }
4561                // eol points to the space between the count and the key
4562                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
4563                int key = eol + 1;  // start of key=value
4564                eol = offset + linelen - 1; // trailing LF
4565                int value;
4566                for (value = key+1; data[value] != '=' && value <= eol; value++);
4567                if (value > eol) {
4568                    throw new IOException("Invalid pax declaration");
4569                }
4570
4571                // pax requires that key/value strings be in UTF-8
4572                String keyStr = new String(data, key, value-key, "UTF-8");
4573                // -1 to strip the trailing LF
4574                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
4575
4576                if ("path".equals(keyStr)) {
4577                    info.path = valStr;
4578                } else if ("size".equals(keyStr)) {
4579                    info.size = Long.parseLong(valStr);
4580                } else {
4581                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
4582                }
4583
4584                offset += linelen;
4585            } while (offset < contentSize);
4586
4587            return true;
4588        }
4589
4590        long extractRadix(byte[] data, int offset, int maxChars, int radix)
4591                throws IOException {
4592            long value = 0;
4593            final int end = offset + maxChars;
4594            for (int i = offset; i < end; i++) {
4595                final byte b = data[i];
4596                // Numeric fields in tar can terminate with either NUL or SPC
4597                if (b == 0 || b == ' ') break;
4598                if (b < '0' || b > ('0' + radix - 1)) {
4599                    throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix);
4600                }
4601                value = radix * value + (b - '0');
4602            }
4603            return value;
4604        }
4605
4606        String extractString(byte[] data, int offset, int maxChars) throws IOException {
4607            final int end = offset + maxChars;
4608            int eos = offset;
4609            // tar string fields terminate early with a NUL
4610            while (eos < end && data[eos] != 0) eos++;
4611            return new String(data, offset, eos-offset, "US-ASCII");
4612        }
4613
4614        void sendStartRestore() {
4615            if (mObserver != null) {
4616                try {
4617                    mObserver.onStartRestore();
4618                } catch (RemoteException e) {
4619                    Slog.w(TAG, "full restore observer went away: startRestore");
4620                    mObserver = null;
4621                }
4622            }
4623        }
4624
4625        void sendOnRestorePackage(String name) {
4626            if (mObserver != null) {
4627                try {
4628                    // TODO: use a more user-friendly name string
4629                    mObserver.onRestorePackage(name);
4630                } catch (RemoteException e) {
4631                    Slog.w(TAG, "full restore observer went away: restorePackage");
4632                    mObserver = null;
4633                }
4634            }
4635        }
4636
4637        void sendEndRestore() {
4638            if (mObserver != null) {
4639                try {
4640                    mObserver.onEndRestore();
4641                } catch (RemoteException e) {
4642                    Slog.w(TAG, "full restore observer went away: endRestore");
4643                    mObserver = null;
4644                }
4645            }
4646        }
4647    }
4648
4649    // ----- Restore handling -----
4650
4651    private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
4652        // If the target resides on the system partition, we allow it to restore
4653        // data from the like-named package in a restore set even if the signatures
4654        // do not match.  (Unlike general applications, those flashed to the system
4655        // partition will be signed with the device's platform certificate, so on
4656        // different phones the same system app will have different signatures.)
4657        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
4658            if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
4659            return true;
4660        }
4661
4662        // Allow unsigned apps, but not signed on one device and unsigned on the other
4663        // !!! TODO: is this the right policy?
4664        Signature[] deviceSigs = target.signatures;
4665        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
4666                + " device=" + deviceSigs);
4667        if ((storedSigs == null || storedSigs.length == 0)
4668                && (deviceSigs == null || deviceSigs.length == 0)) {
4669            return true;
4670        }
4671        if (storedSigs == null || deviceSigs == null) {
4672            return false;
4673        }
4674
4675        // !!! TODO: this demands that every stored signature match one
4676        // that is present on device, and does not demand the converse.
4677        // Is this this right policy?
4678        int nStored = storedSigs.length;
4679        int nDevice = deviceSigs.length;
4680
4681        for (int i=0; i < nStored; i++) {
4682            boolean match = false;
4683            for (int j=0; j < nDevice; j++) {
4684                if (storedSigs[i].equals(deviceSigs[j])) {
4685                    match = true;
4686                    break;
4687                }
4688            }
4689            if (!match) {
4690                return false;
4691            }
4692        }
4693        return true;
4694    }
4695
4696    enum RestoreState {
4697        INITIAL,
4698        DOWNLOAD_DATA,
4699        PM_METADATA,
4700        RUNNING_QUEUE,
4701        FINAL
4702    }
4703
4704    class PerformRestoreTask implements BackupRestoreTask {
4705        private IBackupTransport mTransport;
4706        private IRestoreObserver mObserver;
4707        private long mToken;
4708        private PackageInfo mTargetPackage;
4709        private File mStateDir;
4710        private int mPmToken;
4711        private boolean mIsSystemRestore;
4712        private HashSet<String> mFilterSet;
4713        private long mStartRealtime;
4714        private PackageManagerBackupAgent mPmAgent;
4715        private List<PackageInfo> mAgentPackages;
4716        private ArrayList<PackageInfo> mRestorePackages;
4717        private RestoreState mCurrentState;
4718        private int mCount;
4719        private boolean mFinished;
4720        private int mStatus;
4721        private File mBackupDataName;
4722        private File mStageName;
4723        private File mNewStateName;
4724        private File mSavedStateName;
4725        private ParcelFileDescriptor mBackupData;
4726        private ParcelFileDescriptor mNewState;
4727        private PackageInfo mCurrentPackage;
4728        private byte[] mWidgetData;
4729
4730
4731        class RestoreRequest {
4732            public PackageInfo app;
4733            public int storedAppVersion;
4734
4735            RestoreRequest(PackageInfo _app, int _version) {
4736                app = _app;
4737                storedAppVersion = _version;
4738            }
4739        }
4740
4741        PerformRestoreTask(IBackupTransport transport, String dirName, IRestoreObserver observer,
4742                long restoreSetToken, PackageInfo targetPackage, int pmToken,
4743                boolean isSystemRestore, String[] filterSet) {
4744            mCurrentState = RestoreState.INITIAL;
4745            mFinished = false;
4746            mPmAgent = null;
4747
4748            mTransport = transport;
4749            mObserver = observer;
4750            mToken = restoreSetToken;
4751            mTargetPackage = targetPackage;
4752            mPmToken = pmToken;
4753            mIsSystemRestore = isSystemRestore;
4754
4755            if (filterSet != null) {
4756                mFilterSet = new HashSet<String>();
4757                for (String pkg : filterSet) {
4758                    mFilterSet.add(pkg);
4759                }
4760            } else {
4761                mFilterSet = null;
4762            }
4763
4764            mStateDir = new File(mBaseStateDir, dirName);
4765        }
4766
4767        // Execute one tick of whatever state machine the task implements
4768        @Override
4769        public void execute() {
4770            if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step: " + mCurrentState);
4771            switch (mCurrentState) {
4772                case INITIAL:
4773                    beginRestore();
4774                    break;
4775
4776                case DOWNLOAD_DATA:
4777                    downloadRestoreData();
4778                    break;
4779
4780                case PM_METADATA:
4781                    restorePmMetadata();
4782                    break;
4783
4784                case RUNNING_QUEUE:
4785                    restoreNextAgent();
4786                    break;
4787
4788                case FINAL:
4789                    if (!mFinished) finalizeRestore();
4790                    else {
4791                        Slog.e(TAG, "Duplicate finish");
4792                    }
4793                    mFinished = true;
4794                    break;
4795            }
4796        }
4797
4798        // Initialize and set up for the PM metadata restore, which comes first
4799        void beginRestore() {
4800            // Don't account time doing the restore as inactivity of the app
4801            // that has opened a restore session.
4802            mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
4803
4804            // Assume error until we successfully init everything
4805            mStatus = BackupConstants.TRANSPORT_ERROR;
4806
4807            try {
4808                // TODO: Log this before getAvailableRestoreSets, somehow
4809                EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken);
4810
4811                // Get the list of all packages which have backup enabled.
4812                // (Include the Package Manager metadata pseudo-package first.)
4813                mRestorePackages = new ArrayList<PackageInfo>();
4814                PackageInfo omPackage = new PackageInfo();
4815                omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
4816                mRestorePackages.add(omPackage);
4817
4818                mAgentPackages = allAgentPackages();
4819                if (mTargetPackage == null) {
4820                    // if there's a filter set, strip out anything that isn't
4821                    // present before proceeding
4822                    if (mFilterSet != null) {
4823                        for (int i = mAgentPackages.size() - 1; i >= 0; i--) {
4824                            final PackageInfo pkg = mAgentPackages.get(i);
4825                            if (! mFilterSet.contains(pkg.packageName)) {
4826                                mAgentPackages.remove(i);
4827                            }
4828                        }
4829                        if (MORE_DEBUG) {
4830                            Slog.i(TAG, "Post-filter package set for restore:");
4831                            for (PackageInfo p : mAgentPackages) {
4832                                Slog.i(TAG, "    " + p);
4833                            }
4834                        }
4835                    }
4836                    mRestorePackages.addAll(mAgentPackages);
4837                } else {
4838                    // Just one package to attempt restore of
4839                    mRestorePackages.add(mTargetPackage);
4840                }
4841
4842                // let the observer know that we're running
4843                if (mObserver != null) {
4844                    try {
4845                        // !!! TODO: get an actual count from the transport after
4846                        // its startRestore() runs?
4847                        mObserver.restoreStarting(mRestorePackages.size());
4848                    } catch (RemoteException e) {
4849                        Slog.d(TAG, "Restore observer died at restoreStarting");
4850                        mObserver = null;
4851                    }
4852                }
4853            } catch (RemoteException e) {
4854                // Something has gone catastrophically wrong with the transport
4855                Slog.e(TAG, "Error communicating with transport for restore");
4856                executeNextState(RestoreState.FINAL);
4857                return;
4858            }
4859
4860            mStatus = BackupConstants.TRANSPORT_OK;
4861            executeNextState(RestoreState.DOWNLOAD_DATA);
4862        }
4863
4864        void downloadRestoreData() {
4865            // Note that the download phase can be very time consuming, but we're executing
4866            // it inline here on the looper.  This is "okay" because it is not calling out to
4867            // third party code; the transport is "trusted," and so we assume it is being a
4868            // good citizen and timing out etc when appropriate.
4869            //
4870            // TODO: when appropriate, move the download off the looper and rearrange the
4871            //       error handling around that.
4872            try {
4873                mStatus = mTransport.startRestore(mToken,
4874                        mRestorePackages.toArray(new PackageInfo[0]));
4875                if (mStatus != BackupConstants.TRANSPORT_OK) {
4876                    Slog.e(TAG, "Error starting restore operation");
4877                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
4878                    executeNextState(RestoreState.FINAL);
4879                    return;
4880                }
4881            } catch (RemoteException e) {
4882                Slog.e(TAG, "Error communicating with transport for restore");
4883                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
4884                mStatus = BackupConstants.TRANSPORT_ERROR;
4885                executeNextState(RestoreState.FINAL);
4886                return;
4887            }
4888
4889            // Successful download of the data to be parceled out to the apps, so off we go.
4890            executeNextState(RestoreState.PM_METADATA);
4891        }
4892
4893        void restorePmMetadata() {
4894            try {
4895                String packageName = mTransport.nextRestorePackage();
4896                if (packageName == null) {
4897                    Slog.e(TAG, "Error getting first restore package");
4898                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
4899                    mStatus = BackupConstants.TRANSPORT_ERROR;
4900                    executeNextState(RestoreState.FINAL);
4901                    return;
4902                } else if (packageName.equals("")) {
4903                    Slog.i(TAG, "No restore data available");
4904                    int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
4905                    EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
4906                    mStatus = BackupConstants.TRANSPORT_OK;
4907                    executeNextState(RestoreState.FINAL);
4908                    return;
4909                } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
4910                    Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
4911                            + "\", found only \"" + packageName + "\"");
4912                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
4913                            "Package manager data missing");
4914                    executeNextState(RestoreState.FINAL);
4915                    return;
4916                }
4917
4918                // Pull the Package Manager metadata from the restore set first
4919                PackageInfo omPackage = new PackageInfo();
4920                omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
4921                mPmAgent = new PackageManagerBackupAgent(
4922                        mPackageManager, mAgentPackages);
4923                initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
4924                // The PM agent called operationComplete() already, because our invocation
4925                // of it is process-local and therefore synchronous.  That means that a
4926                // RUNNING_QUEUE message is already enqueued.  Only if we're unable to
4927                // proceed with running the queue do we remove that pending message and
4928                // jump straight to the FINAL state.
4929
4930                // Verify that the backup set includes metadata.  If not, we can't do
4931                // signature/version verification etc, so we simply do not proceed with
4932                // the restore operation.
4933                if (!mPmAgent.hasMetadata()) {
4934                    Slog.e(TAG, "No restore metadata available, so not restoring settings");
4935                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
4936                    "Package manager restore metadata missing");
4937                    mStatus = BackupConstants.TRANSPORT_ERROR;
4938                    mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
4939                    executeNextState(RestoreState.FINAL);
4940                    return;
4941                }
4942            } catch (RemoteException e) {
4943                Slog.e(TAG, "Error communicating with transport for restore");
4944                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
4945                mStatus = BackupConstants.TRANSPORT_ERROR;
4946                mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
4947                executeNextState(RestoreState.FINAL);
4948                return;
4949            }
4950
4951            // Metadata is intact, so we can now run the restore queue.  If we get here,
4952            // we have already enqueued the necessary next-step message on the looper.
4953            // We've deferred telling the App Widget service that we might be replacing
4954            // the widget environment with something else, but now we know we've got
4955            // data coming, so we do it here.
4956            if (mIsSystemRestore) {
4957                AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER);
4958            }
4959        }
4960
4961        void restoreNextAgent() {
4962            try {
4963                String packageName = mTransport.nextRestorePackage();
4964
4965                if (packageName == null) {
4966                    Slog.e(TAG, "Error getting next restore package");
4967                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
4968                    executeNextState(RestoreState.FINAL);
4969                    return;
4970                } else if (packageName.equals("")) {
4971                    if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
4972                    int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
4973                    EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
4974                    executeNextState(RestoreState.FINAL);
4975                    return;
4976                }
4977
4978                if (mObserver != null) {
4979                    try {
4980                        mObserver.onUpdate(mCount, packageName);
4981                    } catch (RemoteException e) {
4982                        Slog.d(TAG, "Restore observer died in onUpdate");
4983                        mObserver = null;
4984                    }
4985                }
4986
4987                Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
4988                if (metaInfo == null) {
4989                    Slog.e(TAG, "Missing metadata for " + packageName);
4990                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
4991                            "Package metadata missing");
4992                    executeNextState(RestoreState.RUNNING_QUEUE);
4993                    return;
4994                }
4995
4996                PackageInfo packageInfo;
4997                try {
4998                    int flags = PackageManager.GET_SIGNATURES;
4999                    packageInfo = mPackageManager.getPackageInfo(packageName, flags);
5000                } catch (NameNotFoundException e) {
5001                    Slog.e(TAG, "Invalid package restoring data", e);
5002                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
5003                            "Package missing on device");
5004                    executeNextState(RestoreState.RUNNING_QUEUE);
5005                    return;
5006                }
5007
5008                if (packageInfo.applicationInfo.backupAgentName == null
5009                        || "".equals(packageInfo.applicationInfo.backupAgentName)) {
5010                    if (DEBUG) {
5011                        Slog.i(TAG, "Data exists for package " + packageName
5012                                + " but app has no agent; skipping");
5013                    }
5014                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
5015                            "Package has no agent");
5016                    executeNextState(RestoreState.RUNNING_QUEUE);
5017                    return;
5018                }
5019
5020                if (metaInfo.versionCode > packageInfo.versionCode) {
5021                    // Data is from a "newer" version of the app than we have currently
5022                    // installed.  If the app has not declared that it is prepared to
5023                    // handle this case, we do not attempt the restore.
5024                    if ((packageInfo.applicationInfo.flags
5025                            & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
5026                        String message = "Version " + metaInfo.versionCode
5027                        + " > installed version " + packageInfo.versionCode;
5028                        Slog.w(TAG, "Package " + packageName + ": " + message);
5029                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
5030                                packageName, message);
5031                        executeNextState(RestoreState.RUNNING_QUEUE);
5032                        return;
5033                    } else {
5034                        if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
5035                                + " > installed " + packageInfo.versionCode
5036                                + " but restoreAnyVersion");
5037                    }
5038                }
5039
5040                if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
5041                    Slog.w(TAG, "Signature mismatch restoring " + packageName);
5042                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
5043                            "Signature mismatch");
5044                    executeNextState(RestoreState.RUNNING_QUEUE);
5045                    return;
5046                }
5047
5048                if (DEBUG) Slog.v(TAG, "Package " + packageName
5049                        + " restore version [" + metaInfo.versionCode
5050                        + "] is compatible with installed version ["
5051                        + packageInfo.versionCode + "]");
5052
5053                // Then set up and bind the agent
5054                IBackupAgent agent = bindToAgentSynchronous(
5055                        packageInfo.applicationInfo,
5056                        IApplicationThread.BACKUP_MODE_INCREMENTAL);
5057                if (agent == null) {
5058                    Slog.w(TAG, "Can't find backup agent for " + packageName);
5059                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
5060                            "Restore agent missing");
5061                    executeNextState(RestoreState.RUNNING_QUEUE);
5062                    return;
5063                }
5064
5065                // And then finally start the restore on this agent
5066                try {
5067                    initiateOneRestore(packageInfo, metaInfo.versionCode, agent);
5068                    ++mCount;
5069                } catch (Exception e) {
5070                    Slog.e(TAG, "Error when attempting restore: " + e.toString());
5071                    agentErrorCleanup();
5072                    executeNextState(RestoreState.RUNNING_QUEUE);
5073                }
5074            } catch (RemoteException e) {
5075                Slog.e(TAG, "Unable to fetch restore data from transport");
5076                mStatus = BackupConstants.TRANSPORT_ERROR;
5077                executeNextState(RestoreState.FINAL);
5078            }
5079        }
5080
5081        void finalizeRestore() {
5082            if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
5083
5084            try {
5085                mTransport.finishRestore();
5086            } catch (RemoteException e) {
5087                Slog.e(TAG, "Error finishing restore", e);
5088            }
5089
5090            if (mObserver != null) {
5091                try {
5092                    mObserver.restoreFinished(mStatus);
5093                } catch (RemoteException e) {
5094                    Slog.d(TAG, "Restore observer died at restoreFinished");
5095                }
5096            }
5097
5098            // If this was a restoreAll operation, record that this was our
5099            // ancestral dataset, as well as the set of apps that are possibly
5100            // restoreable from the dataset
5101            if (mTargetPackage == null && mPmAgent != null) {
5102                mAncestralPackages = mPmAgent.getRestoredPackages();
5103                mAncestralToken = mToken;
5104                writeRestoreTokens();
5105            }
5106
5107            // We must under all circumstances tell the Package Manager to
5108            // proceed with install notifications if it's waiting for us.
5109            if (mPmToken > 0) {
5110                if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
5111                try {
5112                    mPackageManagerBinder.finishPackageInstall(mPmToken);
5113                } catch (RemoteException e) { /* can't happen */ }
5114            }
5115
5116            // Furthermore we need to reset the session timeout clock
5117            mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
5118            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
5119                    TIMEOUT_RESTORE_INTERVAL);
5120
5121            // Kick off any work that may be needed regarding app widget restores
5122            AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER);
5123
5124            // done; we can finally release the wakelock
5125            Slog.i(TAG, "Restore complete.");
5126            mWakelock.release();
5127        }
5128
5129        // Call asynchronously into the app, passing it the restore data.  The next step
5130        // after this is always a callback, either operationComplete() or handleTimeout().
5131        void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
5132            mCurrentPackage = app;
5133            mWidgetData = null;
5134            final String packageName = app.packageName;
5135
5136            if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
5137
5138            // !!! TODO: get the dirs from the transport
5139            mBackupDataName = new File(mDataDir, packageName + ".restore");
5140            mStageName = new File(mDataDir, packageName + ".stage");
5141            mNewStateName = new File(mStateDir, packageName + ".new");
5142            mSavedStateName = new File(mStateDir, packageName);
5143
5144            // don't stage the 'android' package where the wallpaper data lives.  this is
5145            // an optimization: we know there's no widget data hosted/published by that
5146            // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
5147            // data following the download.
5148            boolean staging = !packageName.equals("android");
5149            ParcelFileDescriptor stage;
5150            File downloadFile = (staging) ? mStageName : mBackupDataName;
5151
5152            final int token = generateToken();
5153            try {
5154                // Run the transport's restore pass
5155                stage = ParcelFileDescriptor.open(downloadFile,
5156                            ParcelFileDescriptor.MODE_READ_WRITE |
5157                            ParcelFileDescriptor.MODE_CREATE |
5158                            ParcelFileDescriptor.MODE_TRUNCATE);
5159
5160                if (!SELinux.restorecon(mBackupDataName)) {
5161                    Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
5162                }
5163
5164                if (mTransport.getRestoreData(stage) != BackupConstants.TRANSPORT_OK) {
5165                    // Transport-level failure, so we wind everything up and
5166                    // terminate the restore operation.
5167                    Slog.e(TAG, "Error getting restore data for " + packageName);
5168                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
5169                    stage.close();
5170                    downloadFile.delete();
5171                    executeNextState(RestoreState.FINAL);
5172                    return;
5173                }
5174
5175                // We have the data from the transport. Now we extract and strip
5176                // any per-package metadata (typically widget-related information)
5177                // if appropriate
5178                if (staging) {
5179                    stage.close();
5180                    stage = ParcelFileDescriptor.open(downloadFile,
5181                            ParcelFileDescriptor.MODE_READ_ONLY);
5182
5183                    mBackupData = ParcelFileDescriptor.open(mBackupDataName,
5184                            ParcelFileDescriptor.MODE_READ_WRITE |
5185                            ParcelFileDescriptor.MODE_CREATE |
5186                            ParcelFileDescriptor.MODE_TRUNCATE);
5187
5188                    BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
5189                    BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
5190                    byte[] buffer = new byte[8192]; // will grow when needed
5191                    while (in.readNextHeader()) {
5192                        final String key = in.getKey();
5193                        final int size = in.getDataSize();
5194
5195                        // is this a special key?
5196                        if (key.equals(KEY_WIDGET_STATE)) {
5197                            if (DEBUG) {
5198                                Slog.i(TAG, "Restoring widget state for " + packageName);
5199                            }
5200                            mWidgetData = new byte[size];
5201                            in.readEntityData(mWidgetData, 0, size);
5202                        } else {
5203                            if (size > buffer.length) {
5204                                buffer = new byte[size];
5205                            }
5206                            in.readEntityData(buffer, 0, size);
5207                            out.writeEntityHeader(key, size);
5208                            out.writeEntityData(buffer, size);
5209                        }
5210                    }
5211
5212                    mBackupData.close();
5213                }
5214
5215                // Okay, we have the data.  Now have the agent do the restore.
5216                stage.close();
5217                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
5218                            ParcelFileDescriptor.MODE_READ_ONLY);
5219
5220                mNewState = ParcelFileDescriptor.open(mNewStateName,
5221                            ParcelFileDescriptor.MODE_READ_WRITE |
5222                            ParcelFileDescriptor.MODE_CREATE |
5223                            ParcelFileDescriptor.MODE_TRUNCATE);
5224
5225                // Kick off the restore, checking for hung agents
5226                prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
5227                agent.doRestore(mBackupData, appVersionCode, mNewState,
5228                        token, mBackupManagerBinder);
5229            } catch (Exception e) {
5230                Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
5231                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
5232                agentErrorCleanup();    // clears any pending timeout messages as well
5233
5234                // After a restore failure we go back to running the queue.  If there
5235                // are no more packages to be restored that will be handled by the
5236                // next step.
5237                executeNextState(RestoreState.RUNNING_QUEUE);
5238            }
5239        }
5240
5241        void agentErrorCleanup() {
5242            // If the agent fails restore, it might have put the app's data
5243            // into an incoherent state.  For consistency we wipe its data
5244            // again in this case before continuing with normal teardown
5245            clearApplicationDataSynchronous(mCurrentPackage.packageName);
5246            agentCleanup();
5247        }
5248
5249        void agentCleanup() {
5250            mBackupDataName.delete();
5251            mStageName.delete();
5252            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
5253            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
5254            mBackupData = mNewState = null;
5255
5256            // if everything went okay, remember the recorded state now
5257            //
5258            // !!! TODO: the restored data should be migrated on the server
5259            // side into the current dataset.  In that case the new state file
5260            // we just created would reflect the data already extant in the
5261            // backend, so there'd be nothing more to do.  Until that happens,
5262            // however, we need to make sure that we record the data to the
5263            // current backend dataset.  (Yes, this means shipping the data over
5264            // the wire in both directions.  That's bad, but consistency comes
5265            // first, then efficiency.)  Once we introduce server-side data
5266            // migration to the newly-restored device's dataset, we will change
5267            // the following from a discard of the newly-written state to the
5268            // "correct" operation of renaming into the canonical state blob.
5269            mNewStateName.delete();                      // TODO: remove; see above comment
5270            //mNewStateName.renameTo(mSavedStateName);   // TODO: replace with this
5271
5272            // If this wasn't the PM pseudopackage, tear down the agent side
5273            if (mCurrentPackage.applicationInfo != null) {
5274                // unbind and tidy up even on timeout or failure
5275                try {
5276                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
5277
5278                    // The agent was probably running with a stub Application object,
5279                    // which isn't a valid run mode for the main app logic.  Shut
5280                    // down the app so that next time it's launched, it gets the
5281                    // usual full initialization.  Note that this is only done for
5282                    // full-system restores: when a single app has requested a restore,
5283                    // it is explicitly not killed following that operation.
5284                    if (mTargetPackage == null && (mCurrentPackage.applicationInfo.flags
5285                            & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
5286                        if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
5287                                + mCurrentPackage.applicationInfo.processName);
5288                        mActivityManager.killApplicationProcess(
5289                                mCurrentPackage.applicationInfo.processName,
5290                                mCurrentPackage.applicationInfo.uid);
5291                    }
5292                } catch (RemoteException e) {
5293                    // can't happen; we run in the same process as the activity manager
5294                }
5295            }
5296
5297            // The caller is responsible for reestablishing the state machine; our
5298            // responsibility here is to clear the decks for whatever comes next.
5299            mBackupHandler.removeMessages(MSG_TIMEOUT, this);
5300            synchronized (mCurrentOpLock) {
5301                mCurrentOperations.clear();
5302            }
5303        }
5304
5305        // A call to agent.doRestore() has been positively acknowledged as complete
5306        @Override
5307        public void operationComplete() {
5308            int size = (int) mBackupDataName.length();
5309            EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
5310
5311            // Just go back to running the restore queue
5312            agentCleanup();
5313
5314            // If there was widget state associated with this app, get the OS to
5315            // incorporate it into current bookeeping and then pass that along to
5316            // the app as part of the restore operation.
5317            if (mWidgetData != null) {
5318                restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
5319            }
5320
5321            executeNextState(RestoreState.RUNNING_QUEUE);
5322        }
5323
5324        // A call to agent.doRestore() has timed out
5325        @Override
5326        public void handleTimeout() {
5327            Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
5328            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
5329                    mCurrentPackage.packageName, "restore timeout");
5330            // Handle like an agent that threw on invocation: wipe it and go on to the next
5331            agentErrorCleanup();
5332            executeNextState(RestoreState.RUNNING_QUEUE);
5333        }
5334
5335        void executeNextState(RestoreState nextState) {
5336            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
5337                    + this + " nextState=" + nextState);
5338            mCurrentState = nextState;
5339            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
5340            mBackupHandler.sendMessage(msg);
5341        }
5342    }
5343
5344    // Used by both incremental and full restore
5345    void restoreWidgetData(String packageName, byte[] widgetData) {
5346        // Apply the restored widget state and generate the ID update for the app
5347        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER);
5348    }
5349
5350    class PerformClearTask implements Runnable {
5351        IBackupTransport mTransport;
5352        PackageInfo mPackage;
5353
5354        PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
5355            mTransport = transport;
5356            mPackage = packageInfo;
5357        }
5358
5359        public void run() {
5360            try {
5361                // Clear the on-device backup state to ensure a full backup next time
5362                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
5363                File stateFile = new File(stateDir, mPackage.packageName);
5364                stateFile.delete();
5365
5366                // Tell the transport to remove all the persistent storage for the app
5367                // TODO - need to handle failures
5368                mTransport.clearBackupData(mPackage);
5369            } catch (RemoteException e) {
5370                // can't happen; the transport is local
5371            } catch (Exception e) {
5372                Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
5373            } finally {
5374                try {
5375                    // TODO - need to handle failures
5376                    mTransport.finishBackup();
5377                } catch (RemoteException e) {
5378                    // can't happen; the transport is local
5379                }
5380
5381                // Last but not least, release the cpu
5382                mWakelock.release();
5383            }
5384        }
5385    }
5386
5387    class PerformInitializeTask implements Runnable {
5388        HashSet<String> mQueue;
5389
5390        PerformInitializeTask(HashSet<String> transportNames) {
5391            mQueue = transportNames;
5392        }
5393
5394        public void run() {
5395            try {
5396                for (String transportName : mQueue) {
5397                    IBackupTransport transport = getTransport(transportName);
5398                    if (transport == null) {
5399                        Slog.e(TAG, "Requested init for " + transportName + " but not found");
5400                        continue;
5401                    }
5402
5403                    Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
5404                    EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
5405                    long startRealtime = SystemClock.elapsedRealtime();
5406                    int status = transport.initializeDevice();
5407
5408                    if (status == BackupConstants.TRANSPORT_OK) {
5409                        status = transport.finishBackup();
5410                    }
5411
5412                    // Okay, the wipe really happened.  Clean up our local bookkeeping.
5413                    if (status == BackupConstants.TRANSPORT_OK) {
5414                        Slog.i(TAG, "Device init successful");
5415                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
5416                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
5417                        resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
5418                        EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
5419                        synchronized (mQueueLock) {
5420                            recordInitPendingLocked(false, transportName);
5421                        }
5422                    } else {
5423                        // If this didn't work, requeue this one and try again
5424                        // after a suitable interval
5425                        Slog.e(TAG, "Transport error in initializeDevice()");
5426                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
5427                        synchronized (mQueueLock) {
5428                            recordInitPendingLocked(true, transportName);
5429                        }
5430                        // do this via another alarm to make sure of the wakelock states
5431                        long delay = transport.requestBackupTime();
5432                        if (DEBUG) Slog.w(TAG, "init failed on "
5433                                + transportName + " resched in " + delay);
5434                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
5435                                System.currentTimeMillis() + delay, mRunInitIntent);
5436                    }
5437                }
5438            } catch (RemoteException e) {
5439                // can't happen; the transports are local
5440            } catch (Exception e) {
5441                Slog.e(TAG, "Unexpected error performing init", e);
5442            } finally {
5443                // Done; release the wakelock
5444                mWakelock.release();
5445            }
5446        }
5447    }
5448
5449    private void dataChangedImpl(String packageName) {
5450        HashSet<String> targets = dataChangedTargets(packageName);
5451        dataChangedImpl(packageName, targets);
5452    }
5453
5454    private void dataChangedImpl(String packageName, HashSet<String> targets) {
5455        // Record that we need a backup pass for the caller.  Since multiple callers
5456        // may share a uid, we need to note all candidates within that uid and schedule
5457        // a backup pass for each of them.
5458        EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
5459
5460        if (targets == null) {
5461            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
5462                   + " uid=" + Binder.getCallingUid());
5463            return;
5464        }
5465
5466        synchronized (mQueueLock) {
5467            // Note that this client has made data changes that need to be backed up
5468            if (targets.contains(packageName)) {
5469                // Add the caller to the set of pending backups.  If there is
5470                // one already there, then overwrite it, but no harm done.
5471                BackupRequest req = new BackupRequest(packageName);
5472                if (mPendingBackups.put(packageName, req) == null) {
5473                    if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
5474
5475                    // Journal this request in case of crash.  The put()
5476                    // operation returned null when this package was not already
5477                    // in the set; we want to avoid touching the disk redundantly.
5478                    writeToJournalLocked(packageName);
5479
5480                    if (MORE_DEBUG) {
5481                        int numKeys = mPendingBackups.size();
5482                        Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
5483                        for (BackupRequest b : mPendingBackups.values()) {
5484                            Slog.d(TAG, "    + " + b);
5485                        }
5486                    }
5487                }
5488            }
5489        }
5490    }
5491
5492    // Note: packageName is currently unused, but may be in the future
5493    private HashSet<String> dataChangedTargets(String packageName) {
5494        // If the caller does not hold the BACKUP permission, it can only request a
5495        // backup of its own data.
5496        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
5497                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
5498            synchronized (mBackupParticipants) {
5499                return mBackupParticipants.get(Binder.getCallingUid());
5500            }
5501        }
5502
5503        // a caller with full permission can ask to back up any participating app
5504        // !!! TODO: allow backup of ANY app?
5505        HashSet<String> targets = new HashSet<String>();
5506        synchronized (mBackupParticipants) {
5507            int N = mBackupParticipants.size();
5508            for (int i = 0; i < N; i++) {
5509                HashSet<String> s = mBackupParticipants.valueAt(i);
5510                if (s != null) {
5511                    targets.addAll(s);
5512                }
5513            }
5514        }
5515        return targets;
5516    }
5517
5518    private void writeToJournalLocked(String str) {
5519        RandomAccessFile out = null;
5520        try {
5521            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
5522            out = new RandomAccessFile(mJournal, "rws");
5523            out.seek(out.length());
5524            out.writeUTF(str);
5525        } catch (IOException e) {
5526            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
5527            mJournal = null;
5528        } finally {
5529            try { if (out != null) out.close(); } catch (IOException e) {}
5530        }
5531    }
5532
5533    // ----- IBackupManager binder interface -----
5534
5535    public void dataChanged(final String packageName) {
5536        final int callingUserHandle = UserHandle.getCallingUserId();
5537        if (callingUserHandle != UserHandle.USER_OWNER) {
5538            // App is running under a non-owner user profile.  For now, we do not back
5539            // up data from secondary user profiles.
5540            // TODO: backups for all user profiles.
5541            if (MORE_DEBUG) {
5542                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
5543                        + callingUserHandle);
5544            }
5545            return;
5546        }
5547
5548        final HashSet<String> targets = dataChangedTargets(packageName);
5549        if (targets == null) {
5550            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
5551                   + " uid=" + Binder.getCallingUid());
5552            return;
5553        }
5554
5555        mBackupHandler.post(new Runnable() {
5556                public void run() {
5557                    dataChangedImpl(packageName, targets);
5558                }
5559            });
5560    }
5561
5562    // Clear the given package's backup data from the current transport
5563    public void clearBackupData(String transportName, String packageName) {
5564        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
5565        PackageInfo info;
5566        try {
5567            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
5568        } catch (NameNotFoundException e) {
5569            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
5570            return;
5571        }
5572
5573        // If the caller does not hold the BACKUP permission, it can only request a
5574        // wipe of its own backed-up data.
5575        HashSet<String> apps;
5576        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
5577                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
5578            apps = mBackupParticipants.get(Binder.getCallingUid());
5579        } else {
5580            // a caller with full permission can ask to back up any participating app
5581            // !!! TODO: allow data-clear of ANY app?
5582            if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
5583            apps = new HashSet<String>();
5584            int N = mBackupParticipants.size();
5585            for (int i = 0; i < N; i++) {
5586                HashSet<String> s = mBackupParticipants.valueAt(i);
5587                if (s != null) {
5588                    apps.addAll(s);
5589                }
5590            }
5591        }
5592
5593        // Is the given app an available participant?
5594        if (apps.contains(packageName)) {
5595            // found it; fire off the clear request
5596            if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
5597            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
5598            synchronized (mQueueLock) {
5599                final IBackupTransport transport = getTransport(transportName);
5600                if (transport == null) {
5601                    // transport is currently unavailable -- make sure to retry
5602                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
5603                            new ClearRetryParams(transportName, packageName));
5604                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
5605                    return;
5606                }
5607                long oldId = Binder.clearCallingIdentity();
5608                mWakelock.acquire();
5609                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
5610                        new ClearParams(transport, info));
5611                mBackupHandler.sendMessage(msg);
5612                Binder.restoreCallingIdentity(oldId);
5613            }
5614        }
5615    }
5616
5617    // Run a backup pass immediately for any applications that have declared
5618    // that they have pending updates.
5619    public void backupNow() {
5620        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
5621
5622        if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
5623        synchronized (mQueueLock) {
5624            // Because the alarms we are using can jitter, and we want an *immediate*
5625            // backup pass to happen, we restart the timer beginning with "next time,"
5626            // then manually fire the backup trigger intent ourselves.
5627            startBackupAlarmsLocked(BACKUP_INTERVAL);
5628            try {
5629                mRunBackupIntent.send();
5630            } catch (PendingIntent.CanceledException e) {
5631                // should never happen
5632                Slog.e(TAG, "run-backup intent cancelled!");
5633            }
5634        }
5635    }
5636
5637    boolean deviceIsProvisioned() {
5638        final ContentResolver resolver = mContext.getContentResolver();
5639        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
5640    }
5641
5642    // Run a *full* backup pass for the given package, writing the resulting data stream
5643    // to the supplied file descriptor.  This method is synchronous and does not return
5644    // to the caller until the backup has been completed.
5645    @Override
5646    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
5647            boolean includeObbs, boolean includeShared, boolean doWidgets,
5648            boolean doAllApps, boolean includeSystem, String[] pkgList) {
5649        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
5650
5651        final int callingUserHandle = UserHandle.getCallingUserId();
5652        if (callingUserHandle != UserHandle.USER_OWNER) {
5653            throw new IllegalStateException("Backup supported only for the device owner");
5654        }
5655
5656        // Validate
5657        if (!doAllApps) {
5658            if (!includeShared) {
5659                // If we're backing up shared data (sdcard or equivalent), then we can run
5660                // without any supplied app names.  Otherwise, we'd be doing no work, so
5661                // report the error.
5662                if (pkgList == null || pkgList.length == 0) {
5663                    throw new IllegalArgumentException(
5664                            "Backup requested but neither shared nor any apps named");
5665                }
5666            }
5667        }
5668
5669        long oldId = Binder.clearCallingIdentity();
5670        try {
5671            // Doesn't make sense to do a full backup prior to setup
5672            if (!deviceIsProvisioned()) {
5673                Slog.i(TAG, "Full backup not supported before setup");
5674                return;
5675            }
5676
5677            if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
5678                    + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
5679                    + " pkgs=" + pkgList);
5680            Slog.i(TAG, "Beginning full backup...");
5681
5682            FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
5683                    includeShared, doWidgets, doAllApps, includeSystem, pkgList);
5684            final int token = generateToken();
5685            synchronized (mFullConfirmations) {
5686                mFullConfirmations.put(token, params);
5687            }
5688
5689            // start up the confirmation UI
5690            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
5691            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
5692                Slog.e(TAG, "Unable to launch full backup confirmation");
5693                mFullConfirmations.delete(token);
5694                return;
5695            }
5696
5697            // make sure the screen is lit for the user interaction
5698            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
5699
5700            // start the confirmation countdown
5701            startConfirmationTimeout(token, params);
5702
5703            // wait for the backup to be performed
5704            if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
5705            waitForCompletion(params);
5706        } finally {
5707            try {
5708                fd.close();
5709            } catch (IOException e) {
5710                // just eat it
5711            }
5712            Binder.restoreCallingIdentity(oldId);
5713            Slog.d(TAG, "Full backup processing complete.");
5714        }
5715    }
5716
5717    public void fullRestore(ParcelFileDescriptor fd) {
5718        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
5719
5720        final int callingUserHandle = UserHandle.getCallingUserId();
5721        if (callingUserHandle != UserHandle.USER_OWNER) {
5722            throw new IllegalStateException("Restore supported only for the device owner");
5723        }
5724
5725        long oldId = Binder.clearCallingIdentity();
5726
5727        try {
5728            // Check whether the device has been provisioned -- we don't handle
5729            // full restores prior to completing the setup process.
5730            if (!deviceIsProvisioned()) {
5731                Slog.i(TAG, "Full restore not permitted before setup");
5732                return;
5733            }
5734
5735            Slog.i(TAG, "Beginning full restore...");
5736
5737            FullRestoreParams params = new FullRestoreParams(fd);
5738            final int token = generateToken();
5739            synchronized (mFullConfirmations) {
5740                mFullConfirmations.put(token, params);
5741            }
5742
5743            // start up the confirmation UI
5744            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
5745            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
5746                Slog.e(TAG, "Unable to launch full restore confirmation");
5747                mFullConfirmations.delete(token);
5748                return;
5749            }
5750
5751            // make sure the screen is lit for the user interaction
5752            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
5753
5754            // start the confirmation countdown
5755            startConfirmationTimeout(token, params);
5756
5757            // wait for the restore to be performed
5758            if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
5759            waitForCompletion(params);
5760        } finally {
5761            try {
5762                fd.close();
5763            } catch (IOException e) {
5764                Slog.w(TAG, "Error trying to close fd after full restore: " + e);
5765            }
5766            Binder.restoreCallingIdentity(oldId);
5767            Slog.i(TAG, "Full restore processing complete.");
5768        }
5769    }
5770
5771    boolean startConfirmationUi(int token, String action) {
5772        try {
5773            Intent confIntent = new Intent(action);
5774            confIntent.setClassName("com.android.backupconfirm",
5775                    "com.android.backupconfirm.BackupRestoreConfirmation");
5776            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
5777            confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
5778            mContext.startActivity(confIntent);
5779        } catch (ActivityNotFoundException e) {
5780            return false;
5781        }
5782        return true;
5783    }
5784
5785    void startConfirmationTimeout(int token, FullParams params) {
5786        if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
5787                + TIMEOUT_FULL_CONFIRMATION + " millis");
5788        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
5789                token, 0, params);
5790        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
5791    }
5792
5793    void waitForCompletion(FullParams params) {
5794        synchronized (params.latch) {
5795            while (params.latch.get() == false) {
5796                try {
5797                    params.latch.wait();
5798                } catch (InterruptedException e) { /* never interrupted */ }
5799            }
5800        }
5801    }
5802
5803    void signalFullBackupRestoreCompletion(FullParams params) {
5804        synchronized (params.latch) {
5805            params.latch.set(true);
5806            params.latch.notifyAll();
5807        }
5808    }
5809
5810    // Confirm that the previously-requested full backup/restore operation can proceed.  This
5811    // is used to require a user-facing disclosure about the operation.
5812    @Override
5813    public void acknowledgeFullBackupOrRestore(int token, boolean allow,
5814            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
5815        if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
5816                + " allow=" + allow);
5817
5818        // TODO: possibly require not just this signature-only permission, but even
5819        // require that the specific designated confirmation-UI app uid is the caller?
5820        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
5821
5822        long oldId = Binder.clearCallingIdentity();
5823        try {
5824
5825            FullParams params;
5826            synchronized (mFullConfirmations) {
5827                params = mFullConfirmations.get(token);
5828                if (params != null) {
5829                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
5830                    mFullConfirmations.delete(token);
5831
5832                    if (allow) {
5833                        final int verb = params instanceof FullBackupParams
5834                                ? MSG_RUN_FULL_BACKUP
5835                                : MSG_RUN_FULL_RESTORE;
5836
5837                        params.observer = observer;
5838                        params.curPassword = curPassword;
5839
5840                        boolean isEncrypted;
5841                        try {
5842                            isEncrypted = (mMountService.getEncryptionState() !=
5843                                    IMountService.ENCRYPTION_STATE_NONE);
5844                            if (isEncrypted) Slog.w(TAG, "Device is encrypted; forcing enc password");
5845                        } catch (RemoteException e) {
5846                            // couldn't contact the mount service; fail "safe" and assume encryption
5847                            Slog.e(TAG, "Unable to contact mount service!");
5848                            isEncrypted = true;
5849                        }
5850                        params.encryptPassword = (isEncrypted) ? curPassword : encPpassword;
5851
5852                        if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
5853                        mWakelock.acquire();
5854                        Message msg = mBackupHandler.obtainMessage(verb, params);
5855                        mBackupHandler.sendMessage(msg);
5856                    } else {
5857                        Slog.w(TAG, "User rejected full backup/restore operation");
5858                        // indicate completion without having actually transferred any data
5859                        signalFullBackupRestoreCompletion(params);
5860                    }
5861                } else {
5862                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
5863                }
5864            }
5865        } finally {
5866            Binder.restoreCallingIdentity(oldId);
5867        }
5868    }
5869
5870    // Enable/disable the backup service
5871    @Override
5872    public void setBackupEnabled(boolean enable) {
5873        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
5874                "setBackupEnabled");
5875
5876        Slog.i(TAG, "Backup enabled => " + enable);
5877
5878        long oldId = Binder.clearCallingIdentity();
5879        try {
5880            boolean wasEnabled = mEnabled;
5881            synchronized (this) {
5882                Settings.Secure.putInt(mContext.getContentResolver(),
5883                        Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
5884                mEnabled = enable;
5885            }
5886
5887            synchronized (mQueueLock) {
5888                if (enable && !wasEnabled && mProvisioned) {
5889                    // if we've just been enabled, start scheduling backup passes
5890                    startBackupAlarmsLocked(BACKUP_INTERVAL);
5891                } else if (!enable) {
5892                    // No longer enabled, so stop running backups
5893                    if (DEBUG) Slog.i(TAG, "Opting out of backup");
5894
5895                    mAlarmManager.cancel(mRunBackupIntent);
5896
5897                    // This also constitutes an opt-out, so we wipe any data for
5898                    // this device from the backend.  We start that process with
5899                    // an alarm in order to guarantee wakelock states.
5900                    if (wasEnabled && mProvisioned) {
5901                        // NOTE: we currently flush every registered transport, not just
5902                        // the currently-active one.
5903                        HashSet<String> allTransports;
5904                        synchronized (mTransports) {
5905                            allTransports = new HashSet<String>(mTransports.keySet());
5906                        }
5907                        // build the set of transports for which we are posting an init
5908                        for (String transport : allTransports) {
5909                            recordInitPendingLocked(true, transport);
5910                        }
5911                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
5912                                mRunInitIntent);
5913                    }
5914                }
5915            }
5916        } finally {
5917            Binder.restoreCallingIdentity(oldId);
5918        }
5919    }
5920
5921    // Enable/disable automatic restore of app data at install time
5922    public void setAutoRestore(boolean doAutoRestore) {
5923        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
5924                "setAutoRestore");
5925
5926        Slog.i(TAG, "Auto restore => " + doAutoRestore);
5927
5928        synchronized (this) {
5929            Settings.Secure.putInt(mContext.getContentResolver(),
5930                    Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
5931            mAutoRestore = doAutoRestore;
5932        }
5933    }
5934
5935    // Mark the backup service as having been provisioned
5936    public void setBackupProvisioned(boolean available) {
5937        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
5938                "setBackupProvisioned");
5939        /*
5940         * This is now a no-op; provisioning is simply the device's own setup state.
5941         */
5942    }
5943
5944    private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
5945        // We used to use setInexactRepeating(), but that may be linked to
5946        // backups running at :00 more often than not, creating load spikes.
5947        // Schedule at an exact time for now, and also add a bit of "fuzz".
5948
5949        Random random = new Random();
5950        long when = System.currentTimeMillis() + delayBeforeFirstBackup +
5951                random.nextInt(FUZZ_MILLIS);
5952        mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
5953                BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
5954        mNextBackupPass = when;
5955    }
5956
5957    // Report whether the backup mechanism is currently enabled
5958    public boolean isBackupEnabled() {
5959        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
5960        return mEnabled;    // no need to synchronize just to read it
5961    }
5962
5963    // Report the name of the currently active transport
5964    public String getCurrentTransport() {
5965        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
5966                "getCurrentTransport");
5967        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
5968        return mCurrentTransport;
5969    }
5970
5971    // Report all known, available backup transports
5972    public String[] listAllTransports() {
5973        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
5974
5975        String[] list = null;
5976        ArrayList<String> known = new ArrayList<String>();
5977        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
5978            if (entry.getValue() != null) {
5979                known.add(entry.getKey());
5980            }
5981        }
5982
5983        if (known.size() > 0) {
5984            list = new String[known.size()];
5985            known.toArray(list);
5986        }
5987        return list;
5988    }
5989
5990    // Select which transport to use for the next backup operation.  If the given
5991    // name is not one of the available transports, no action is taken and the method
5992    // returns null.
5993    public String selectBackupTransport(String transport) {
5994        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
5995
5996        synchronized (mTransports) {
5997            String prevTransport = null;
5998            if (mTransports.get(transport) != null) {
5999                prevTransport = mCurrentTransport;
6000                mCurrentTransport = transport;
6001                Settings.Secure.putString(mContext.getContentResolver(),
6002                        Settings.Secure.BACKUP_TRANSPORT, transport);
6003                Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
6004                        + " returning " + prevTransport);
6005            } else {
6006                Slog.w(TAG, "Attempt to select unavailable transport " + transport);
6007            }
6008            return prevTransport;
6009        }
6010    }
6011
6012    // Supply the configuration Intent for the given transport.  If the name is not one
6013    // of the available transports, or if the transport does not supply any configuration
6014    // UI, the method returns null.
6015    public Intent getConfigurationIntent(String transportName) {
6016        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
6017                "getConfigurationIntent");
6018
6019        synchronized (mTransports) {
6020            final IBackupTransport transport = mTransports.get(transportName);
6021            if (transport != null) {
6022                try {
6023                    final Intent intent = transport.configurationIntent();
6024                    if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
6025                            + intent);
6026                    return intent;
6027                } catch (RemoteException e) {
6028                    /* fall through to return null */
6029                }
6030            }
6031        }
6032
6033        return null;
6034    }
6035
6036    // Supply the configuration summary string for the given transport.  If the name is
6037    // not one of the available transports, or if the transport does not supply any
6038    // summary / destination string, the method can return null.
6039    //
6040    // This string is used VERBATIM as the summary text of the relevant Settings item!
6041    public String getDestinationString(String transportName) {
6042        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
6043                "getDestinationString");
6044
6045        synchronized (mTransports) {
6046            final IBackupTransport transport = mTransports.get(transportName);
6047            if (transport != null) {
6048                try {
6049                    final String text = transport.currentDestinationString();
6050                    if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
6051                    return text;
6052                } catch (RemoteException e) {
6053                    /* fall through to return null */
6054                }
6055            }
6056        }
6057
6058        return null;
6059    }
6060
6061    // Callback: a requested backup agent has been instantiated.  This should only
6062    // be called from the Activity Manager.
6063    public void agentConnected(String packageName, IBinder agentBinder) {
6064        synchronized(mAgentConnectLock) {
6065            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
6066                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
6067                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
6068                mConnectedAgent = agent;
6069                mConnecting = false;
6070            } else {
6071                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
6072                        + " claiming agent connected");
6073            }
6074            mAgentConnectLock.notifyAll();
6075        }
6076    }
6077
6078    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
6079    // If the agent failed to come up in the first place, the agentBinder argument
6080    // will be null.  This should only be called from the Activity Manager.
6081    public void agentDisconnected(String packageName) {
6082        // TODO: handle backup being interrupted
6083        synchronized(mAgentConnectLock) {
6084            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
6085                mConnectedAgent = null;
6086                mConnecting = false;
6087            } else {
6088                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
6089                        + " claiming agent disconnected");
6090            }
6091            mAgentConnectLock.notifyAll();
6092        }
6093    }
6094
6095    // An application being installed will need a restore pass, then the Package Manager
6096    // will need to be told when the restore is finished.
6097    public void restoreAtInstall(String packageName, int token) {
6098        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
6099            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
6100                    + " attemping install-time restore");
6101            return;
6102        }
6103
6104        boolean skip = false;
6105
6106        long restoreSet = getAvailableRestoreToken(packageName);
6107        if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
6108                + " token=" + Integer.toHexString(token)
6109                + " restoreSet=" + Long.toHexString(restoreSet));
6110        if (restoreSet == 0) {
6111            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
6112            skip = true;
6113        }
6114
6115        // Do we have a transport to fetch data for us?
6116        IBackupTransport transport = getTransport(mCurrentTransport);
6117        if (transport == null) {
6118            if (DEBUG) Slog.w(TAG, "No transport");
6119            skip = true;
6120        }
6121
6122        if (!skip && mAutoRestore && mProvisioned) {
6123            try {
6124                // okay, we're going to attempt a restore of this package from this restore set.
6125                // The eventual message back into the Package Manager to run the post-install
6126                // steps for 'token' will be issued from the restore handling code.
6127
6128                // This can throw and so *must* happen before the wakelock is acquired
6129                String dirName = transport.transportDirName();
6130
6131                // We can use a synthetic PackageInfo here because:
6132                //   1. We know it's valid, since the Package Manager supplied the name
6133                //   2. Only the packageName field will be used by the restore code
6134                PackageInfo pkg = new PackageInfo();
6135                pkg.packageName = packageName;
6136
6137                mWakelock.acquire();
6138                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
6139                msg.obj = new RestoreParams(transport, dirName, null,
6140                        restoreSet, pkg, token);
6141                mBackupHandler.sendMessage(msg);
6142            } catch (RemoteException e) {
6143                // Binding to the transport broke; back off and proceed with the installation.
6144                Slog.e(TAG, "Unable to contact transport");
6145                skip = true;
6146            }
6147        }
6148
6149        if (skip) {
6150            // Auto-restore disabled or no way to attempt a restore; just tell the Package
6151            // Manager to proceed with the post-install handling for this package.
6152            if (DEBUG) Slog.v(TAG, "Skipping");
6153            try {
6154                mPackageManagerBinder.finishPackageInstall(token);
6155            } catch (RemoteException e) { /* can't happen */ }
6156        }
6157    }
6158
6159    // Hand off a restore session
6160    public IRestoreSession beginRestoreSession(String packageName, String transport) {
6161        if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
6162                + " transport=" + transport);
6163
6164        boolean needPermission = true;
6165        if (transport == null) {
6166            transport = mCurrentTransport;
6167
6168            if (packageName != null) {
6169                PackageInfo app = null;
6170                try {
6171                    app = mPackageManager.getPackageInfo(packageName, 0);
6172                } catch (NameNotFoundException nnf) {
6173                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
6174                    throw new IllegalArgumentException("Package " + packageName + " not found");
6175                }
6176
6177                if (app.applicationInfo.uid == Binder.getCallingUid()) {
6178                    // So: using the current active transport, and the caller has asked
6179                    // that its own package will be restored.  In this narrow use case
6180                    // we do not require the caller to hold the permission.
6181                    needPermission = false;
6182                }
6183            }
6184        }
6185
6186        if (needPermission) {
6187            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
6188                    "beginRestoreSession");
6189        } else {
6190            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
6191        }
6192
6193        synchronized(this) {
6194            if (mActiveRestoreSession != null) {
6195                Slog.d(TAG, "Restore session requested but one already active");
6196                return null;
6197            }
6198            mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
6199            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
6200        }
6201        return mActiveRestoreSession;
6202    }
6203
6204    void clearRestoreSession(ActiveRestoreSession currentSession) {
6205        synchronized(this) {
6206            if (currentSession != mActiveRestoreSession) {
6207                Slog.e(TAG, "ending non-current restore session");
6208            } else {
6209                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
6210                mActiveRestoreSession = null;
6211                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
6212            }
6213        }
6214    }
6215
6216    // Note that a currently-active backup agent has notified us that it has
6217    // completed the given outstanding asynchronous backup/restore operation.
6218    @Override
6219    public void opComplete(int token) {
6220        if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
6221        Operation op = null;
6222        synchronized (mCurrentOpLock) {
6223            op = mCurrentOperations.get(token);
6224            if (op != null) {
6225                op.state = OP_ACKNOWLEDGED;
6226            }
6227            mCurrentOpLock.notifyAll();
6228        }
6229
6230        // The completion callback, if any, is invoked on the handler
6231        if (op != null && op.callback != null) {
6232            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
6233            mBackupHandler.sendMessage(msg);
6234        }
6235    }
6236
6237    // ----- Restore session -----
6238
6239    class ActiveRestoreSession extends IRestoreSession.Stub {
6240        private static final String TAG = "RestoreSession";
6241
6242        private String mPackageName;
6243        private IBackupTransport mRestoreTransport = null;
6244        RestoreSet[] mRestoreSets = null;
6245        boolean mEnded = false;
6246
6247        ActiveRestoreSession(String packageName, String transport) {
6248            mPackageName = packageName;
6249            mRestoreTransport = getTransport(transport);
6250        }
6251
6252        // --- Binder interface ---
6253        public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
6254            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
6255                    "getAvailableRestoreSets");
6256            if (observer == null) {
6257                throw new IllegalArgumentException("Observer must not be null");
6258            }
6259
6260            if (mEnded) {
6261                throw new IllegalStateException("Restore session already ended");
6262            }
6263
6264            long oldId = Binder.clearCallingIdentity();
6265            try {
6266                if (mRestoreTransport == null) {
6267                    Slog.w(TAG, "Null transport getting restore sets");
6268                    return -1;
6269                }
6270                // spin off the transport request to our service thread
6271                mWakelock.acquire();
6272                Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
6273                        new RestoreGetSetsParams(mRestoreTransport, this, observer));
6274                mBackupHandler.sendMessage(msg);
6275                return 0;
6276            } catch (Exception e) {
6277                Slog.e(TAG, "Error in getAvailableRestoreSets", e);
6278                return -1;
6279            } finally {
6280                Binder.restoreCallingIdentity(oldId);
6281            }
6282        }
6283
6284        public synchronized int restoreAll(long token, IRestoreObserver observer) {
6285            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
6286                    "performRestore");
6287
6288            if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
6289                    + " observer=" + observer);
6290
6291            if (mEnded) {
6292                throw new IllegalStateException("Restore session already ended");
6293            }
6294
6295            if (mRestoreTransport == null || mRestoreSets == null) {
6296                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
6297                return -1;
6298            }
6299
6300            if (mPackageName != null) {
6301                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
6302                return -1;
6303            }
6304
6305            String dirName;
6306            try {
6307                dirName = mRestoreTransport.transportDirName();
6308            } catch (RemoteException e) {
6309                // Transport went AWOL; fail.
6310                Slog.e(TAG, "Unable to contact transport for restore");
6311                return -1;
6312            }
6313
6314            synchronized (mQueueLock) {
6315                for (int i = 0; i < mRestoreSets.length; i++) {
6316                    if (token == mRestoreSets[i].token) {
6317                        long oldId = Binder.clearCallingIdentity();
6318                        mWakelock.acquire();
6319                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
6320                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
6321                                observer, token);
6322                        mBackupHandler.sendMessage(msg);
6323                        Binder.restoreCallingIdentity(oldId);
6324                        return 0;
6325                    }
6326                }
6327            }
6328
6329            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
6330            return -1;
6331        }
6332
6333        // Restores of more than a single package are treated as 'system' restores
6334        public synchronized int restoreSome(long token, IRestoreObserver observer,
6335                String[] packages) {
6336            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
6337                    "performRestore");
6338
6339            if (DEBUG) {
6340                StringBuilder b = new StringBuilder(128);
6341                b.append("restoreSome token=");
6342                b.append(Long.toHexString(token));
6343                b.append(" observer=");
6344                b.append(observer.toString());
6345                b.append(" packages=");
6346                if (packages == null) {
6347                    b.append("null");
6348                } else {
6349                    b.append('{');
6350                    boolean first = true;
6351                    for (String s : packages) {
6352                        if (!first) {
6353                            b.append(", ");
6354                        } else first = false;
6355                        b.append(s);
6356                    }
6357                    b.append('}');
6358                }
6359                Slog.d(TAG, b.toString());
6360            }
6361
6362            if (mEnded) {
6363                throw new IllegalStateException("Restore session already ended");
6364            }
6365
6366            if (mRestoreTransport == null || mRestoreSets == null) {
6367                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
6368                return -1;
6369            }
6370
6371            if (mPackageName != null) {
6372                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
6373                return -1;
6374            }
6375
6376            String dirName;
6377            try {
6378                dirName = mRestoreTransport.transportDirName();
6379            } catch (RemoteException e) {
6380                // Transport went AWOL; fail.
6381                Slog.e(TAG, "Unable to contact transport for restore");
6382                return -1;
6383            }
6384
6385            synchronized (mQueueLock) {
6386                for (int i = 0; i < mRestoreSets.length; i++) {
6387                    if (token == mRestoreSets[i].token) {
6388                        long oldId = Binder.clearCallingIdentity();
6389                        mWakelock.acquire();
6390                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
6391                        msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
6392                                packages, packages.length > 1);
6393                        mBackupHandler.sendMessage(msg);
6394                        Binder.restoreCallingIdentity(oldId);
6395                        return 0;
6396                    }
6397                }
6398            }
6399
6400            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
6401            return -1;
6402        }
6403
6404        public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
6405            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
6406
6407            if (mEnded) {
6408                throw new IllegalStateException("Restore session already ended");
6409            }
6410
6411            if (mPackageName != null) {
6412                if (! mPackageName.equals(packageName)) {
6413                    Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
6414                            + " on session for package " + mPackageName);
6415                    return -1;
6416                }
6417            }
6418
6419            PackageInfo app = null;
6420            try {
6421                app = mPackageManager.getPackageInfo(packageName, 0);
6422            } catch (NameNotFoundException nnf) {
6423                Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
6424                return -1;
6425            }
6426
6427            // If the caller is not privileged and is not coming from the target
6428            // app's uid, throw a permission exception back to the caller.
6429            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
6430                    Binder.getCallingPid(), Binder.getCallingUid());
6431            if ((perm == PackageManager.PERMISSION_DENIED) &&
6432                    (app.applicationInfo.uid != Binder.getCallingUid())) {
6433                Slog.w(TAG, "restorePackage: bad packageName=" + packageName
6434                        + " or calling uid=" + Binder.getCallingUid());
6435                throw new SecurityException("No permission to restore other packages");
6436            }
6437
6438            // If the package has no backup agent, we obviously cannot proceed
6439            if (app.applicationInfo.backupAgentName == null) {
6440                Slog.w(TAG, "Asked to restore package " + packageName + " with no agent");
6441                return -1;
6442            }
6443
6444            // So far so good; we're allowed to try to restore this package.  Now
6445            // check whether there is data for it in the current dataset, falling back
6446            // to the ancestral dataset if not.
6447            long token = getAvailableRestoreToken(packageName);
6448
6449            // If we didn't come up with a place to look -- no ancestral dataset and
6450            // the app has never been backed up from this device -- there's nothing
6451            // to do but return failure.
6452            if (token == 0) {
6453                if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
6454                return -1;
6455            }
6456
6457            String dirName;
6458            try {
6459                dirName = mRestoreTransport.transportDirName();
6460            } catch (RemoteException e) {
6461                // Transport went AWOL; fail.
6462                Slog.e(TAG, "Unable to contact transport for restore");
6463                return -1;
6464            }
6465
6466            // Ready to go:  enqueue the restore request and claim success
6467            long oldId = Binder.clearCallingIdentity();
6468            mWakelock.acquire();
6469            Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
6470            msg.obj = new RestoreParams(mRestoreTransport, dirName,
6471                    observer, token, app, 0);
6472            mBackupHandler.sendMessage(msg);
6473            Binder.restoreCallingIdentity(oldId);
6474            return 0;
6475        }
6476
6477        // Posted to the handler to tear down a restore session in a cleanly synchronized way
6478        class EndRestoreRunnable implements Runnable {
6479            BackupManagerService mBackupManager;
6480            ActiveRestoreSession mSession;
6481
6482            EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
6483                mBackupManager = manager;
6484                mSession = session;
6485            }
6486
6487            public void run() {
6488                // clean up the session's bookkeeping
6489                synchronized (mSession) {
6490                    try {
6491                        if (mSession.mRestoreTransport != null) {
6492                            mSession.mRestoreTransport.finishRestore();
6493                        }
6494                    } catch (Exception e) {
6495                        Slog.e(TAG, "Error in finishRestore", e);
6496                    } finally {
6497                        mSession.mRestoreTransport = null;
6498                        mSession.mEnded = true;
6499                    }
6500                }
6501
6502                // clean up the BackupManagerImpl side of the bookkeeping
6503                // and cancel any pending timeout message
6504                mBackupManager.clearRestoreSession(mSession);
6505            }
6506        }
6507
6508        public synchronized void endRestoreSession() {
6509            if (DEBUG) Slog.d(TAG, "endRestoreSession");
6510
6511            if (mEnded) {
6512                throw new IllegalStateException("Restore session already ended");
6513            }
6514
6515            mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
6516        }
6517    }
6518
6519    @Override
6520    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
6521        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
6522
6523        long identityToken = Binder.clearCallingIdentity();
6524        try {
6525            dumpInternal(pw);
6526        } finally {
6527            Binder.restoreCallingIdentity(identityToken);
6528        }
6529    }
6530
6531    private void dumpInternal(PrintWriter pw) {
6532        synchronized (mQueueLock) {
6533            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
6534                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
6535                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
6536            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
6537            if (mBackupRunning) pw.println("Backup currently running");
6538            pw.println("Last backup pass started: " + mLastBackupPass
6539                    + " (now = " + System.currentTimeMillis() + ')');
6540            pw.println("  next scheduled: " + mNextBackupPass);
6541
6542            pw.println("Available transports:");
6543            for (String t : listAllTransports()) {
6544                pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
6545                try {
6546                    IBackupTransport transport = getTransport(t);
6547                    File dir = new File(mBaseStateDir, transport.transportDirName());
6548                    pw.println("       destination: " + transport.currentDestinationString());
6549                    pw.println("       intent: " + transport.configurationIntent());
6550                    for (File f : dir.listFiles()) {
6551                        pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
6552                    }
6553                } catch (Exception e) {
6554                    Slog.e(TAG, "Error in transport", e);
6555                    pw.println("        Error: " + e);
6556                }
6557            }
6558
6559            pw.println("Pending init: " + mPendingInits.size());
6560            for (String s : mPendingInits) {
6561                pw.println("    " + s);
6562            }
6563
6564            if (DEBUG_BACKUP_TRACE) {
6565                synchronized (mBackupTrace) {
6566                    if (!mBackupTrace.isEmpty()) {
6567                        pw.println("Most recent backup trace:");
6568                        for (String s : mBackupTrace) {
6569                            pw.println("   " + s);
6570                        }
6571                    }
6572                }
6573            }
6574
6575            int N = mBackupParticipants.size();
6576            pw.println("Participants:");
6577            for (int i=0; i<N; i++) {
6578                int uid = mBackupParticipants.keyAt(i);
6579                pw.print("  uid: ");
6580                pw.println(uid);
6581                HashSet<String> participants = mBackupParticipants.valueAt(i);
6582                for (String app: participants) {
6583                    pw.println("    " + app);
6584                }
6585            }
6586
6587            pw.println("Ancestral packages: "
6588                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
6589            if (mAncestralPackages != null) {
6590                for (String pkg : mAncestralPackages) {
6591                    pw.println("    " + pkg);
6592                }
6593            }
6594
6595            pw.println("Ever backed up: " + mEverStoredApps.size());
6596            for (String pkg : mEverStoredApps) {
6597                pw.println("    " + pkg);
6598            }
6599
6600            pw.println("Pending backup: " + mPendingBackups.size());
6601            for (BackupRequest req : mPendingBackups.values()) {
6602                pw.println("    " + req);
6603            }
6604        }
6605    }
6606}
6607