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