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