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