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