BackupManagerService.java revision cffb19c812dd6d619e292519ca5ede61310aeab6
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.backup;
18
19import android.app.ActivityManagerNative;
20import android.app.AlarmManager;
21import android.app.AppGlobals;
22import android.app.IActivityManager;
23import android.app.IApplicationThread;
24import android.app.IBackupAgent;
25import android.app.PendingIntent;
26import android.app.backup.BackupAgent;
27import android.app.backup.BackupDataInput;
28import android.app.backup.BackupDataOutput;
29import android.app.backup.BackupTransport;
30import android.app.backup.FullBackup;
31import android.app.backup.RestoreDescription;
32import android.app.backup.RestoreSet;
33import android.app.backup.IBackupManager;
34import android.app.backup.IFullBackupRestoreObserver;
35import android.app.backup.IRestoreObserver;
36import android.app.backup.IRestoreSession;
37import android.content.ActivityNotFoundException;
38import android.content.BroadcastReceiver;
39import android.content.ComponentName;
40import android.content.ContentResolver;
41import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
44import android.content.ServiceConnection;
45import android.content.pm.ApplicationInfo;
46import android.content.pm.IPackageDataObserver;
47import android.content.pm.IPackageDeleteObserver;
48import android.content.pm.IPackageInstallObserver;
49import android.content.pm.IPackageManager;
50import android.content.pm.PackageInfo;
51import android.content.pm.PackageManager;
52import android.content.pm.ResolveInfo;
53import android.content.pm.ServiceInfo;
54import android.content.pm.Signature;
55import android.content.pm.PackageManager.NameNotFoundException;
56import android.database.ContentObserver;
57import android.net.Uri;
58import android.os.Binder;
59import android.os.Build;
60import android.os.Bundle;
61import android.os.Environment;
62import android.os.Handler;
63import android.os.HandlerThread;
64import android.os.IBinder;
65import android.os.Looper;
66import android.os.Message;
67import android.os.ParcelFileDescriptor;
68import android.os.PowerManager;
69import android.os.Process;
70import android.os.RemoteException;
71import android.os.SELinux;
72import android.os.ServiceManager;
73import android.os.SystemClock;
74import android.os.UserHandle;
75import android.os.WorkSource;
76import android.os.Environment.UserEnvironment;
77import android.os.storage.IMountService;
78import android.os.storage.StorageManager;
79import android.provider.Settings;
80import android.system.ErrnoException;
81import android.system.Os;
82import android.util.ArrayMap;
83import android.util.AtomicFile;
84import android.util.EventLog;
85import android.util.Log;
86import android.util.Slog;
87import android.util.SparseArray;
88import android.util.StringBuilderPrinter;
89
90import com.android.internal.backup.IBackupTransport;
91import com.android.internal.backup.IObbBackupService;
92import com.android.server.AppWidgetBackupBridge;
93import com.android.server.EventLogTags;
94import com.android.server.SystemService;
95import com.android.server.backup.PackageManagerBackupAgent.Metadata;
96
97import java.io.BufferedInputStream;
98import java.io.BufferedOutputStream;
99import java.io.ByteArrayInputStream;
100import java.io.ByteArrayOutputStream;
101import java.io.DataInputStream;
102import java.io.DataOutputStream;
103import java.io.EOFException;
104import java.io.File;
105import java.io.FileDescriptor;
106import java.io.FileInputStream;
107import java.io.FileNotFoundException;
108import java.io.FileOutputStream;
109import java.io.IOException;
110import java.io.InputStream;
111import java.io.OutputStream;
112import java.io.PrintWriter;
113import java.io.RandomAccessFile;
114import java.security.InvalidAlgorithmParameterException;
115import java.security.InvalidKeyException;
116import java.security.Key;
117import java.security.MessageDigest;
118import java.security.NoSuchAlgorithmException;
119import java.security.SecureRandom;
120import java.security.spec.InvalidKeySpecException;
121import java.security.spec.KeySpec;
122import java.text.SimpleDateFormat;
123import java.util.ArrayList;
124import java.util.Arrays;
125import java.util.Collections;
126import java.util.Date;
127import java.util.HashMap;
128import java.util.HashSet;
129import java.util.Iterator;
130import java.util.List;
131import java.util.Map;
132import java.util.Map.Entry;
133import java.util.Objects;
134import java.util.Random;
135import java.util.Set;
136import java.util.TreeMap;
137import java.util.concurrent.atomic.AtomicBoolean;
138import java.util.concurrent.atomic.AtomicInteger;
139import java.util.zip.Deflater;
140import java.util.zip.DeflaterOutputStream;
141import java.util.zip.InflaterInputStream;
142
143import javax.crypto.BadPaddingException;
144import javax.crypto.Cipher;
145import javax.crypto.CipherInputStream;
146import javax.crypto.CipherOutputStream;
147import javax.crypto.IllegalBlockSizeException;
148import javax.crypto.NoSuchPaddingException;
149import javax.crypto.SecretKey;
150import javax.crypto.SecretKeyFactory;
151import javax.crypto.spec.IvParameterSpec;
152import javax.crypto.spec.PBEKeySpec;
153import javax.crypto.spec.SecretKeySpec;
154
155import libcore.io.IoUtils;
156
157public class BackupManagerService {
158
159    private static final String TAG = "BackupManagerService";
160    private static final boolean DEBUG = true;
161    private static final boolean MORE_DEBUG = false;
162    private static final boolean DEBUG_SCHEDULING = MORE_DEBUG || true;
163
164    // System-private key used for backing up an app's widget state.  Must
165    // begin with U+FFxx by convention (we reserve all keys starting
166    // with U+FF00 or higher for system use).
167    static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
168
169    // Historical and current algorithm names
170    static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
171    static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
172
173    // Name and current contents version of the full-backup manifest file
174    //
175    // Manifest version history:
176    //
177    // 1 : initial release
178    static final String BACKUP_MANIFEST_FILENAME = "_manifest";
179    static final int BACKUP_MANIFEST_VERSION = 1;
180
181    // External archive format version history:
182    //
183    // 1 : initial release
184    // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
185    // 3 : introduced "_meta" metadata file; no other format change per se
186    static final int BACKUP_FILE_VERSION = 3;
187    static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
188    static final int BACKUP_PW_FILE_VERSION = 2;
189    static final String BACKUP_METADATA_FILENAME = "_meta";
190    static final int BACKUP_METADATA_VERSION = 1;
191    static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
192    static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
193
194    static final String SETTINGS_PACKAGE = "com.android.providers.settings";
195    static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
196    static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
197
198    // How often we perform a backup pass.  Privileged external callers can
199    // trigger an immediate pass.
200    private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
201
202    // Random variation in backup scheduling time to avoid server load spikes
203    private static final int FUZZ_MILLIS = 5 * 60 * 1000;
204
205    // The amount of time between the initial provisioning of the device and
206    // the first backup pass.
207    private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
208
209    // Retry interval for clear/init when the transport is unavailable
210    private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
211
212    private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
213    private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
214    private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
215    private static final int MSG_RUN_BACKUP = 1;
216    private static final int MSG_RUN_ADB_BACKUP = 2;
217    private static final int MSG_RUN_RESTORE = 3;
218    private static final int MSG_RUN_CLEAR = 4;
219    private static final int MSG_RUN_INITIALIZE = 5;
220    private static final int MSG_RUN_GET_RESTORE_SETS = 6;
221    private static final int MSG_TIMEOUT = 7;
222    private static final int MSG_RESTORE_TIMEOUT = 8;
223    private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
224    private static final int MSG_RUN_ADB_RESTORE = 10;
225    private static final int MSG_RETRY_INIT = 11;
226    private static final int MSG_RETRY_CLEAR = 12;
227    private static final int MSG_WIDGET_BROADCAST = 13;
228    private static final int MSG_RUN_FULL_TRANSPORT_BACKUP = 14;
229
230    // backup task state machine tick
231    static final int MSG_BACKUP_RESTORE_STEP = 20;
232    static final int MSG_OP_COMPLETE = 21;
233
234    // Timeout interval for deciding that a bind or clear-data has taken too long
235    static final long TIMEOUT_INTERVAL = 10 * 1000;
236
237    // Timeout intervals for agent backup & restore operations
238    static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
239    static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
240    static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
241    static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
242    static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
243
244    // User confirmation timeout for a full backup/restore operation.  It's this long in
245    // order to give them time to enter the backup password.
246    static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
247
248    // How long between attempts to perform a full-data backup of any given app
249    static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day
250
251    Context mContext;
252    private PackageManager mPackageManager;
253    IPackageManager mPackageManagerBinder;
254    private IActivityManager mActivityManager;
255    private PowerManager mPowerManager;
256    private AlarmManager mAlarmManager;
257    private IMountService mMountService;
258    IBackupManager mBackupManagerBinder;
259
260    boolean mEnabled;   // access to this is synchronized on 'this'
261    boolean mProvisioned;
262    boolean mAutoRestore;
263    PowerManager.WakeLock mWakelock;
264    HandlerThread mHandlerThread;
265    BackupHandler mBackupHandler;
266    PendingIntent mRunBackupIntent, mRunInitIntent;
267    BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
268    // map UIDs to the set of participating packages under that UID
269    final SparseArray<HashSet<String>> mBackupParticipants
270            = new SparseArray<HashSet<String>>();
271    // set of backup services that have pending changes
272    class BackupRequest {
273        public String packageName;
274
275        BackupRequest(String pkgName) {
276            packageName = pkgName;
277        }
278
279        public String toString() {
280            return "BackupRequest{pkg=" + packageName + "}";
281        }
282    }
283    // Backups that we haven't started yet.  Keys are package names.
284    HashMap<String,BackupRequest> mPendingBackups
285            = new HashMap<String,BackupRequest>();
286
287    // Pseudoname that we use for the Package Manager metadata "package"
288    static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
289
290    // locking around the pending-backup management
291    final Object mQueueLock = new Object();
292
293    // The thread performing the sequence of queued backups binds to each app's agent
294    // in succession.  Bind notifications are asynchronously delivered through the
295    // Activity Manager; use this lock object to signal when a requested binding has
296    // completed.
297    final Object mAgentConnectLock = new Object();
298    IBackupAgent mConnectedAgent;
299    volatile boolean mBackupRunning;
300    volatile boolean mConnecting;
301    volatile long mLastBackupPass;
302    volatile long mNextBackupPass;
303
304    // For debugging, we maintain a progress trace of operations during backup
305    static final boolean DEBUG_BACKUP_TRACE = true;
306    final List<String> mBackupTrace = new ArrayList<String>();
307
308    // A similar synchronization mechanism around clearing apps' data for restore
309    final Object mClearDataLock = new Object();
310    volatile boolean mClearingData;
311
312    // Transport bookkeeping
313    final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
314    final ArrayMap<String,String> mTransportNames
315            = new ArrayMap<String,String>();             // component name -> registration name
316    final ArrayMap<String,IBackupTransport> mTransports
317            = new ArrayMap<String,IBackupTransport>();   // registration name -> binder
318    final ArrayMap<String,TransportConnection> mTransportConnections
319            = new ArrayMap<String,TransportConnection>();
320    String mCurrentTransport;
321    ActiveRestoreSession mActiveRestoreSession;
322
323    // Watch the device provisioning operation during setup
324    ContentObserver mProvisionedObserver;
325
326    // The published binder is actually to a singleton trampoline object that calls
327    // through to the proper code.  This indirection lets us turn down the heavy
328    // implementation object on the fly without disturbing binders that have been
329    // cached elsewhere in the system.
330    static Trampoline sInstance;
331    static Trampoline getInstance() {
332        // Always constructed during system bringup, so no need to lazy-init
333        return sInstance;
334    }
335
336    public static final class Lifecycle extends SystemService {
337
338        public Lifecycle(Context context) {
339            super(context);
340            sInstance = new Trampoline(context);
341        }
342
343        @Override
344        public void onStart() {
345            publishBinderService(Context.BACKUP_SERVICE, sInstance);
346        }
347
348        @Override
349        public void onBootPhase(int phase) {
350            if (phase == PHASE_SYSTEM_SERVICES_READY) {
351                sInstance.initialize(UserHandle.USER_OWNER);
352            } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
353                ContentResolver r = sInstance.mContext.getContentResolver();
354                boolean areEnabled = Settings.Secure.getInt(r,
355                        Settings.Secure.BACKUP_ENABLED, 0) != 0;
356                try {
357                    sInstance.setBackupEnabled(areEnabled);
358                } catch (RemoteException e) {
359                    // can't happen; it's a local object
360                }
361            }
362        }
363    }
364
365    class ProvisionedObserver extends ContentObserver {
366        public ProvisionedObserver(Handler handler) {
367            super(handler);
368        }
369
370        public void onChange(boolean selfChange) {
371            final boolean wasProvisioned = mProvisioned;
372            final boolean isProvisioned = deviceIsProvisioned();
373            // latch: never unprovision
374            mProvisioned = wasProvisioned || isProvisioned;
375            if (MORE_DEBUG) {
376                Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
377                        + " is=" + isProvisioned + " now=" + mProvisioned);
378            }
379
380            synchronized (mQueueLock) {
381                if (mProvisioned && !wasProvisioned && mEnabled) {
382                    // we're now good to go, so start the backup alarms
383                    if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
384                    startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
385                    scheduleNextFullBackupJob();
386                }
387            }
388        }
389    }
390
391    class RestoreGetSetsParams {
392        public IBackupTransport transport;
393        public ActiveRestoreSession session;
394        public IRestoreObserver observer;
395
396        RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
397                IRestoreObserver _observer) {
398            transport = _transport;
399            session = _session;
400            observer = _observer;
401        }
402    }
403
404    class RestoreParams {
405        public IBackupTransport transport;
406        public String dirName;
407        public IRestoreObserver observer;
408        public long token;
409        public PackageInfo pkgInfo;
410        public int pmToken; // in post-install restore, the PM's token for this transaction
411        public boolean isSystemRestore;
412        public String[] filterSet;
413
414        // Restore a single package
415        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
416                long _token, PackageInfo _pkg, int _pmToken) {
417            transport = _transport;
418            dirName = _dirName;
419            observer = _obs;
420            token = _token;
421            pkgInfo = _pkg;
422            pmToken = _pmToken;
423            isSystemRestore = false;
424            filterSet = null;
425        }
426
427        // Restore everything possible.  This is the form that Setup Wizard or similar
428        // restore UXes use.
429        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
430                long _token) {
431            transport = _transport;
432            dirName = _dirName;
433            observer = _obs;
434            token = _token;
435            pkgInfo = null;
436            pmToken = 0;
437            isSystemRestore = true;
438            filterSet = null;
439        }
440
441        // Restore some set of packages.  Leave this one up to the caller to specify
442        // whether it's to be considered a system-level restore.
443        RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
444                long _token, String[] _filterSet, boolean _isSystemRestore) {
445            transport = _transport;
446            dirName = _dirName;
447            observer = _obs;
448            token = _token;
449            pkgInfo = null;
450            pmToken = 0;
451            isSystemRestore = _isSystemRestore;
452            filterSet = _filterSet;
453        }
454    }
455
456    class ClearParams {
457        public IBackupTransport transport;
458        public PackageInfo packageInfo;
459
460        ClearParams(IBackupTransport _transport, PackageInfo _info) {
461            transport = _transport;
462            packageInfo = _info;
463        }
464    }
465
466    class ClearRetryParams {
467        public String transportName;
468        public String packageName;
469
470        ClearRetryParams(String transport, String pkg) {
471            transportName = transport;
472            packageName = pkg;
473        }
474    }
475
476    class FullParams {
477        public ParcelFileDescriptor fd;
478        public final AtomicBoolean latch;
479        public IFullBackupRestoreObserver observer;
480        public String curPassword;     // filled in by the confirmation step
481        public String encryptPassword;
482
483        FullParams() {
484            latch = new AtomicBoolean(false);
485        }
486    }
487
488    class FullBackupParams extends FullParams {
489        public boolean includeApks;
490        public boolean includeObbs;
491        public boolean includeShared;
492        public boolean doWidgets;
493        public boolean allApps;
494        public boolean includeSystem;
495        public boolean doCompress;
496        public String[] packages;
497
498        FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
499                boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem,
500                boolean compress, String[] pkgList) {
501            fd = output;
502            includeApks = saveApks;
503            includeObbs = saveObbs;
504            includeShared = saveShared;
505            doWidgets = alsoWidgets;
506            allApps = doAllApps;
507            includeSystem = doSystem;
508            doCompress = compress;
509            packages = pkgList;
510        }
511    }
512
513    class FullRestoreParams extends FullParams {
514        FullRestoreParams(ParcelFileDescriptor input) {
515            fd = input;
516        }
517    }
518
519    // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
520    // token is the index of the entry in the pending-operations list.
521    static final int OP_PENDING = 0;
522    static final int OP_ACKNOWLEDGED = 1;
523    static final int OP_TIMEOUT = -1;
524
525    class Operation {
526        public int state;
527        public BackupRestoreTask callback;
528
529        Operation(int initialState, BackupRestoreTask callbackObj) {
530            state = initialState;
531            callback = callbackObj;
532        }
533    }
534    final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>();
535    final Object mCurrentOpLock = new Object();
536    final Random mTokenGenerator = new Random();
537
538    final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
539
540    // Where we keep our journal files and other bookkeeping
541    File mBaseStateDir;
542    File mDataDir;
543    File mJournalDir;
544    File mJournal;
545
546    // Backup password, if any, and the file where it's saved.  What is stored is not the
547    // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but
548    // persisted) salt.  Validation is performed by running the challenge text through the
549    // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches
550    // the saved hash string, then the challenge text matches the originally supplied
551    // password text.
552    private final SecureRandom mRng = new SecureRandom();
553    private String mPasswordHash;
554    private File mPasswordHashFile;
555    private int mPasswordVersion;
556    private File mPasswordVersionFile;
557    private byte[] mPasswordSalt;
558
559    // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys
560    static final int PBKDF2_HASH_ROUNDS = 10000;
561    static final int PBKDF2_KEY_SIZE = 256;     // bits
562    static final int PBKDF2_SALT_SIZE = 512;    // bits
563    static final String ENCRYPTION_ALGORITHM_NAME = "AES-256";
564
565    // Keep a log of all the apps we've ever backed up, and what the
566    // dataset tokens are for both the current backup dataset and
567    // the ancestral dataset.
568    private File mEverStored;
569    HashSet<String> mEverStoredApps = new HashSet<String>();
570
571    static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;  // increment when the schema changes
572    File mTokenFile;
573    Set<String> mAncestralPackages = null;
574    long mAncestralToken = 0;
575    long mCurrentToken = 0;
576
577    // Persistently track the need to do a full init
578    static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
579    HashSet<String> mPendingInits = new HashSet<String>();  // transport names
580
581    // Round-robin queue for scheduling full backup passes
582    static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
583    class FullBackupEntry implements Comparable<FullBackupEntry> {
584        String packageName;
585        long lastBackup;
586
587        FullBackupEntry(String pkg, long when) {
588            packageName = pkg;
589            lastBackup = when;
590        }
591
592        @Override
593        public int compareTo(FullBackupEntry other) {
594            if (lastBackup < other.lastBackup) return -1;
595            else if (lastBackup > other.lastBackup) return 1;
596            else return 0;
597        }
598    }
599
600    File mFullBackupScheduleFile;
601    // If we're running a schedule-driven full backup, this is the task instance doing it
602    PerformFullTransportBackupTask mRunningFullBackupTask; // inside mQueueLock
603    ArrayList<FullBackupEntry> mFullBackupQueue;           // inside mQueueLock
604
605    // Utility: build a new random integer token
606    int generateToken() {
607        int token;
608        do {
609            synchronized (mTokenGenerator) {
610                token = mTokenGenerator.nextInt();
611            }
612        } while (token < 0);
613        return token;
614    }
615
616    // High level policy: apps are ineligible for backup if certain conditions apply
617    public static boolean appIsEligibleForBackup(ApplicationInfo app) {
618        // 1. their manifest states android:allowBackup="false"
619        if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
620            return false;
621        }
622
623        // 2. they run as a system-level uid but do not supply their own backup agent
624        if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) {
625            return false;
626        }
627
628        // 3. it is the special shared-storage backup package used for 'adb backup'
629        if (app.packageName.equals(BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE)) {
630            return false;
631        }
632
633        return true;
634    }
635
636    /* does *not* check overall backup eligibility policy! */
637    public static boolean appGetsFullBackup(PackageInfo pkg) {
638        if (pkg.applicationInfo.backupAgentName != null) {
639            // If it has an agent, it gets full backups only if it says so
640            return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0;
641        }
642
643        // No agent means we do full backups for it
644        return true;
645    }
646
647    // ----- Asynchronous backup/restore handler thread -----
648
649    private class BackupHandler extends Handler {
650        public BackupHandler(Looper looper) {
651            super(looper);
652        }
653
654        public void handleMessage(Message msg) {
655
656            switch (msg.what) {
657            case MSG_RUN_BACKUP:
658            {
659                mLastBackupPass = System.currentTimeMillis();
660                mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
661
662                IBackupTransport transport = getTransport(mCurrentTransport);
663                if (transport == null) {
664                    Slog.v(TAG, "Backup requested but no transport available");
665                    synchronized (mQueueLock) {
666                        mBackupRunning = false;
667                    }
668                    mWakelock.release();
669                    break;
670                }
671
672                // snapshot the pending-backup set and work on that
673                ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
674                File oldJournal = mJournal;
675                synchronized (mQueueLock) {
676                    // Do we have any work to do?  Construct the work queue
677                    // then release the synchronization lock to actually run
678                    // the backup.
679                    if (mPendingBackups.size() > 0) {
680                        for (BackupRequest b: mPendingBackups.values()) {
681                            queue.add(b);
682                        }
683                        if (DEBUG) Slog.v(TAG, "clearing pending backups");
684                        mPendingBackups.clear();
685
686                        // Start a new backup-queue journal file too
687                        mJournal = null;
688
689                    }
690                }
691
692                // At this point, we have started a new journal file, and the old
693                // file identity is being passed to the backup processing task.
694                // When it completes successfully, that old journal file will be
695                // deleted.  If we crash prior to that, the old journal is parsed
696                // at next boot and the journaled requests fulfilled.
697                boolean staged = true;
698                if (queue.size() > 0) {
699                    // Spin up a backup state sequence and set it running
700                    try {
701                        String dirName = transport.transportDirName();
702                        PerformBackupTask pbt = new PerformBackupTask(transport, dirName,
703                                queue, oldJournal);
704                        Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
705                        sendMessage(pbtMessage);
706                    } catch (RemoteException e) {
707                        // unable to ask the transport its dir name -- transient failure, since
708                        // the above check succeeded.  Try again next time.
709                        Slog.e(TAG, "Transport became unavailable attempting backup");
710                        staged = false;
711                    }
712                } else {
713                    Slog.v(TAG, "Backup requested but nothing pending");
714                    staged = false;
715                }
716
717                if (!staged) {
718                    // if we didn't actually hand off the wakelock, rewind until next time
719                    synchronized (mQueueLock) {
720                        mBackupRunning = false;
721                    }
722                    mWakelock.release();
723                }
724                break;
725            }
726
727            case MSG_BACKUP_RESTORE_STEP:
728            {
729                try {
730                    BackupRestoreTask task = (BackupRestoreTask) msg.obj;
731                    if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing");
732                    task.execute();
733                } catch (ClassCastException e) {
734                    Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);
735                }
736                break;
737            }
738
739            case MSG_OP_COMPLETE:
740            {
741                try {
742                    BackupRestoreTask task = (BackupRestoreTask) msg.obj;
743                    task.operationComplete();
744                } catch (ClassCastException e) {
745                    Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
746                }
747                break;
748            }
749
750            case MSG_RUN_ADB_BACKUP:
751            {
752                // TODO: refactor full backup to be a looper-based state machine
753                // similar to normal backup/restore.
754                FullBackupParams params = (FullBackupParams)msg.obj;
755                PerformAdbBackupTask task = new PerformAdbBackupTask(params.fd,
756                        params.observer, params.includeApks, params.includeObbs,
757                        params.includeShared, params.doWidgets,
758                        params.curPassword, params.encryptPassword,
759                        params.allApps, params.includeSystem, params.doCompress,
760                        params.packages, params.latch);
761                (new Thread(task, "adb-backup")).start();
762                break;
763            }
764
765            case MSG_RUN_FULL_TRANSPORT_BACKUP:
766            {
767                PerformFullTransportBackupTask task = (PerformFullTransportBackupTask) msg.obj;
768                (new Thread(task, "transport-backup")).start();
769                break;
770            }
771
772            case MSG_RUN_RESTORE:
773            {
774                RestoreParams params = (RestoreParams)msg.obj;
775                Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
776                BackupRestoreTask task = new PerformUnifiedRestoreTask(params.transport,
777                        params.observer, params.token, params.pkgInfo, params.pmToken,
778                        params.isSystemRestore, params.filterSet);
779                Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
780                sendMessage(restoreMsg);
781                break;
782            }
783
784            case MSG_RUN_ADB_RESTORE:
785            {
786                // TODO: refactor full restore to be a looper-based state machine
787                // similar to normal backup/restore.
788                FullRestoreParams params = (FullRestoreParams)msg.obj;
789                PerformAdbRestoreTask task = new PerformAdbRestoreTask(params.fd,
790                        params.curPassword, params.encryptPassword,
791                        params.observer, params.latch);
792                (new Thread(task, "adb-restore")).start();
793                break;
794            }
795
796            case MSG_RUN_CLEAR:
797            {
798                ClearParams params = (ClearParams)msg.obj;
799                (new PerformClearTask(params.transport, params.packageInfo)).run();
800                break;
801            }
802
803            case MSG_RETRY_CLEAR:
804            {
805                // reenqueues if the transport remains unavailable
806                ClearRetryParams params = (ClearRetryParams)msg.obj;
807                clearBackupData(params.transportName, params.packageName);
808                break;
809            }
810
811            case MSG_RUN_INITIALIZE:
812            {
813                HashSet<String> queue;
814
815                // Snapshot the pending-init queue and work on that
816                synchronized (mQueueLock) {
817                    queue = new HashSet<String>(mPendingInits);
818                    mPendingInits.clear();
819                }
820
821                (new PerformInitializeTask(queue)).run();
822                break;
823            }
824
825            case MSG_RETRY_INIT:
826            {
827                synchronized (mQueueLock) {
828                    recordInitPendingLocked(msg.arg1 != 0, (String)msg.obj);
829                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
830                            mRunInitIntent);
831                }
832                break;
833            }
834
835            case MSG_RUN_GET_RESTORE_SETS:
836            {
837                // Like other async operations, this is entered with the wakelock held
838                RestoreSet[] sets = null;
839                RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
840                try {
841                    sets = params.transport.getAvailableRestoreSets();
842                    // cache the result in the active session
843                    synchronized (params.session) {
844                        params.session.mRestoreSets = sets;
845                    }
846                    if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
847                } catch (Exception e) {
848                    Slog.e(TAG, "Error from transport getting set list");
849                } finally {
850                    if (params.observer != null) {
851                        try {
852                            params.observer.restoreSetsAvailable(sets);
853                        } catch (RemoteException re) {
854                            Slog.e(TAG, "Unable to report listing to observer");
855                        } catch (Exception e) {
856                            Slog.e(TAG, "Restore observer threw", e);
857                        }
858                    }
859
860                    // Done: reset the session timeout clock
861                    removeMessages(MSG_RESTORE_TIMEOUT);
862                    sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
863
864                    mWakelock.release();
865                }
866                break;
867            }
868
869            case MSG_TIMEOUT:
870            {
871                handleTimeout(msg.arg1, msg.obj);
872                break;
873            }
874
875            case MSG_RESTORE_TIMEOUT:
876            {
877                synchronized (BackupManagerService.this) {
878                    if (mActiveRestoreSession != null) {
879                        // Client app left the restore session dangling.  We know that it
880                        // can't be in the middle of an actual restore operation because
881                        // the timeout is suspended while a restore is in progress.  Clean
882                        // up now.
883                        Slog.w(TAG, "Restore session timed out; aborting");
884                        mActiveRestoreSession.markTimedOut();
885                        post(mActiveRestoreSession.new EndRestoreRunnable(
886                                BackupManagerService.this, mActiveRestoreSession));
887                    }
888                }
889                break;
890            }
891
892            case MSG_FULL_CONFIRMATION_TIMEOUT:
893            {
894                synchronized (mFullConfirmations) {
895                    FullParams params = mFullConfirmations.get(msg.arg1);
896                    if (params != null) {
897                        Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
898
899                        // Release the waiter; timeout == completion
900                        signalFullBackupRestoreCompletion(params);
901
902                        // Remove the token from the set
903                        mFullConfirmations.delete(msg.arg1);
904
905                        // Report a timeout to the observer, if any
906                        if (params.observer != null) {
907                            try {
908                                params.observer.onTimeout();
909                            } catch (RemoteException e) {
910                                /* don't care if the app has gone away */
911                            }
912                        }
913                    } else {
914                        Slog.d(TAG, "couldn't find params for token " + msg.arg1);
915                    }
916                }
917                break;
918            }
919
920            case MSG_WIDGET_BROADCAST:
921            {
922                final Intent intent = (Intent) msg.obj;
923                mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
924                break;
925            }
926            }
927        }
928    }
929
930    // ----- Debug-only backup operation trace -----
931    void addBackupTrace(String s) {
932        if (DEBUG_BACKUP_TRACE) {
933            synchronized (mBackupTrace) {
934                mBackupTrace.add(s);
935            }
936        }
937    }
938
939    void clearBackupTrace() {
940        if (DEBUG_BACKUP_TRACE) {
941            synchronized (mBackupTrace) {
942                mBackupTrace.clear();
943            }
944        }
945    }
946
947    // ----- Main service implementation -----
948
949    public BackupManagerService(Context context, Trampoline parent) {
950        mContext = context;
951        mPackageManager = context.getPackageManager();
952        mPackageManagerBinder = AppGlobals.getPackageManager();
953        mActivityManager = ActivityManagerNative.getDefault();
954
955        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
956        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
957        mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
958
959        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
960
961        // spin up the backup/restore handler thread
962        mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
963        mHandlerThread.start();
964        mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
965
966        // Set up our bookkeeping
967        final ContentResolver resolver = context.getContentResolver();
968        mProvisioned = Settings.Global.getInt(resolver,
969                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
970        mAutoRestore = Settings.Secure.getInt(resolver,
971                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
972
973        mProvisionedObserver = new ProvisionedObserver(mBackupHandler);
974        resolver.registerContentObserver(
975                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
976                false, mProvisionedObserver);
977
978        // If Encrypted file systems is enabled or disabled, this call will return the
979        // correct directory.
980        mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
981        mBaseStateDir.mkdirs();
982        if (!SELinux.restorecon(mBaseStateDir)) {
983            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
984        }
985        mDataDir = Environment.getDownloadCacheDirectory();
986
987        mPasswordVersion = 1;       // unless we hear otherwise
988        mPasswordVersionFile = new File(mBaseStateDir, "pwversion");
989        if (mPasswordVersionFile.exists()) {
990            FileInputStream fin = null;
991            DataInputStream in = null;
992            try {
993                fin = new FileInputStream(mPasswordVersionFile);
994                in = new DataInputStream(fin);
995                mPasswordVersion = in.readInt();
996            } catch (IOException e) {
997                Slog.e(TAG, "Unable to read backup pw version");
998            } finally {
999                try {
1000                    if (in != null) in.close();
1001                    if (fin != null) fin.close();
1002                } catch (IOException e) {
1003                    Slog.w(TAG, "Error closing pw version files");
1004                }
1005            }
1006        }
1007
1008        mPasswordHashFile = new File(mBaseStateDir, "pwhash");
1009        if (mPasswordHashFile.exists()) {
1010            FileInputStream fin = null;
1011            DataInputStream in = null;
1012            try {
1013                fin = new FileInputStream(mPasswordHashFile);
1014                in = new DataInputStream(new BufferedInputStream(fin));
1015                // integer length of the salt array, followed by the salt,
1016                // then the hex pw hash string
1017                int saltLen = in.readInt();
1018                byte[] salt = new byte[saltLen];
1019                in.readFully(salt);
1020                mPasswordHash = in.readUTF();
1021                mPasswordSalt = salt;
1022            } catch (IOException e) {
1023                Slog.e(TAG, "Unable to read saved backup pw hash");
1024            } finally {
1025                try {
1026                    if (in != null) in.close();
1027                    if (fin != null) fin.close();
1028                } catch (IOException e) {
1029                    Slog.w(TAG, "Unable to close streams");
1030                }
1031            }
1032        }
1033
1034        // Alarm receivers for scheduled backups & initialization operations
1035        mRunBackupReceiver = new RunBackupReceiver();
1036        IntentFilter filter = new IntentFilter();
1037        filter.addAction(RUN_BACKUP_ACTION);
1038        context.registerReceiver(mRunBackupReceiver, filter,
1039                android.Manifest.permission.BACKUP, null);
1040
1041        mRunInitReceiver = new RunInitializeReceiver();
1042        filter = new IntentFilter();
1043        filter.addAction(RUN_INITIALIZE_ACTION);
1044        context.registerReceiver(mRunInitReceiver, filter,
1045                android.Manifest.permission.BACKUP, null);
1046
1047        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
1048        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
1049        mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
1050
1051        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
1052        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
1053        mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
1054
1055        // Set up the backup-request journaling
1056        mJournalDir = new File(mBaseStateDir, "pending");
1057        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
1058        mJournal = null;        // will be created on first use
1059
1060        // Set up the various sorts of package tracking we do
1061        mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
1062        initPackageTracking();
1063
1064        // Build our mapping of uid to backup client services.  This implicitly
1065        // schedules a backup pass on the Package Manager metadata the first
1066        // time anything needs to be backed up.
1067        synchronized (mBackupParticipants) {
1068            addPackageParticipantsLocked(null);
1069        }
1070
1071        // Set up our transport options and initialize the default transport
1072        // TODO: Don't create transports that we don't need to?
1073        mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
1074                Settings.Secure.BACKUP_TRANSPORT);
1075        if ("".equals(mCurrentTransport)) {
1076            mCurrentTransport = null;
1077        }
1078        if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
1079
1080        // Find all transport hosts and bind to their services
1081        List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
1082                mTransportServiceIntent, 0, UserHandle.USER_OWNER);
1083        if (DEBUG) {
1084            Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
1085        }
1086        if (hosts != null) {
1087            for (int i = 0; i < hosts.size(); i++) {
1088                final ServiceInfo transport = hosts.get(i).serviceInfo;
1089                if (MORE_DEBUG) {
1090                    Slog.v(TAG, "   " + transport.packageName + "/" + transport.name);
1091                }
1092                tryBindTransport(transport);
1093            }
1094        }
1095
1096        // Now that we know about valid backup participants, parse any
1097        // leftover journal files into the pending backup set
1098        parseLeftoverJournals();
1099
1100        // Power management
1101        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
1102    }
1103
1104    private class RunBackupReceiver extends BroadcastReceiver {
1105        public void onReceive(Context context, Intent intent) {
1106            if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
1107                synchronized (mQueueLock) {
1108                    if (mPendingInits.size() > 0) {
1109                        // If there are pending init operations, we process those
1110                        // and then settle into the usual periodic backup schedule.
1111                        if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
1112                        try {
1113                            mAlarmManager.cancel(mRunInitIntent);
1114                            mRunInitIntent.send();
1115                        } catch (PendingIntent.CanceledException ce) {
1116                            Slog.e(TAG, "Run init intent cancelled");
1117                            // can't really do more than bail here
1118                        }
1119                    } else {
1120                        // Don't run backups now if we're disabled or not yet
1121                        // fully set up.
1122                        if (mEnabled && mProvisioned) {
1123                            if (!mBackupRunning) {
1124                                if (DEBUG) Slog.v(TAG, "Running a backup pass");
1125
1126                                // Acquire the wakelock and pass it to the backup thread.  it will
1127                                // be released once backup concludes.
1128                                mBackupRunning = true;
1129                                mWakelock.acquire();
1130
1131                                Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
1132                                mBackupHandler.sendMessage(msg);
1133                            } else {
1134                                Slog.i(TAG, "Backup time but one already running");
1135                            }
1136                        } else {
1137                            Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
1138                        }
1139                    }
1140                }
1141            }
1142        }
1143    }
1144
1145    private class RunInitializeReceiver extends BroadcastReceiver {
1146        public void onReceive(Context context, Intent intent) {
1147            if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
1148                synchronized (mQueueLock) {
1149                    if (DEBUG) Slog.v(TAG, "Running a device init");
1150
1151                    // Acquire the wakelock and pass it to the init thread.  it will
1152                    // be released once init concludes.
1153                    mWakelock.acquire();
1154
1155                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
1156                    mBackupHandler.sendMessage(msg);
1157                }
1158            }
1159        }
1160    }
1161
1162    private void initPackageTracking() {
1163        if (MORE_DEBUG) Slog.v(TAG, "` tracking");
1164
1165        // Remember our ancestral dataset
1166        mTokenFile = new File(mBaseStateDir, "ancestral");
1167        try {
1168            RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
1169            int version = tf.readInt();
1170            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
1171                mAncestralToken = tf.readLong();
1172                mCurrentToken = tf.readLong();
1173
1174                int numPackages = tf.readInt();
1175                if (numPackages >= 0) {
1176                    mAncestralPackages = new HashSet<String>();
1177                    for (int i = 0; i < numPackages; i++) {
1178                        String pkgName = tf.readUTF();
1179                        mAncestralPackages.add(pkgName);
1180                    }
1181                }
1182            }
1183            tf.close();
1184        } catch (FileNotFoundException fnf) {
1185            // Probably innocuous
1186            Slog.v(TAG, "No ancestral data");
1187        } catch (IOException e) {
1188            Slog.w(TAG, "Unable to read token file", e);
1189        }
1190
1191        // Keep a log of what apps we've ever backed up.  Because we might have
1192        // rebooted in the middle of an operation that was removing something from
1193        // this log, we sanity-check its contents here and reconstruct it.
1194        mEverStored = new File(mBaseStateDir, "processed");
1195        File tempProcessedFile = new File(mBaseStateDir, "processed.new");
1196
1197        // If we were in the middle of removing something from the ever-backed-up
1198        // file, there might be a transient "processed.new" file still present.
1199        // Ignore it -- we'll validate "processed" against the current package set.
1200        if (tempProcessedFile.exists()) {
1201            tempProcessedFile.delete();
1202        }
1203
1204        // If there are previous contents, parse them out then start a new
1205        // file to continue the recordkeeping.
1206        if (mEverStored.exists()) {
1207            RandomAccessFile temp = null;
1208            RandomAccessFile in = null;
1209
1210            try {
1211                temp = new RandomAccessFile(tempProcessedFile, "rws");
1212                in = new RandomAccessFile(mEverStored, "r");
1213
1214                while (true) {
1215                    PackageInfo info;
1216                    String pkg = in.readUTF();
1217                    try {
1218                        info = mPackageManager.getPackageInfo(pkg, 0);
1219                        mEverStoredApps.add(pkg);
1220                        temp.writeUTF(pkg);
1221                        if (MORE_DEBUG) Slog.v(TAG, "   + " + pkg);
1222                    } catch (NameNotFoundException e) {
1223                        // nope, this package was uninstalled; don't include it
1224                        if (MORE_DEBUG) Slog.v(TAG, "   - " + pkg);
1225                    }
1226                }
1227            } catch (EOFException e) {
1228                // Once we've rewritten the backup history log, atomically replace the
1229                // old one with the new one then reopen the file for continuing use.
1230                if (!tempProcessedFile.renameTo(mEverStored)) {
1231                    Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
1232                }
1233            } catch (IOException e) {
1234                Slog.e(TAG, "Error in processed file", e);
1235            } finally {
1236                try { if (temp != null) temp.close(); } catch (IOException e) {}
1237                try { if (in != null) in.close(); } catch (IOException e) {}
1238            }
1239        }
1240
1241        // Resume the full-data backup queue
1242        mFullBackupQueue = readFullBackupSchedule();
1243
1244        // Register for broadcasts about package install, etc., so we can
1245        // update the provider list.
1246        IntentFilter filter = new IntentFilter();
1247        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
1248        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1249        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1250        filter.addDataScheme("package");
1251        mContext.registerReceiver(mBroadcastReceiver, filter);
1252        // Register for events related to sdcard installation.
1253        IntentFilter sdFilter = new IntentFilter();
1254        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
1255        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1256        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
1257    }
1258
1259    private ArrayList<FullBackupEntry> readFullBackupSchedule() {
1260        ArrayList<FullBackupEntry> schedule = null;
1261        synchronized (mQueueLock) {
1262            if (mFullBackupScheduleFile.exists()) {
1263                FileInputStream fstream = null;
1264                BufferedInputStream bufStream = null;
1265                DataInputStream in = null;
1266                try {
1267                    fstream = new FileInputStream(mFullBackupScheduleFile);
1268                    bufStream = new BufferedInputStream(fstream);
1269                    in = new DataInputStream(bufStream);
1270
1271                    int version = in.readInt();
1272                    if (version != SCHEDULE_FILE_VERSION) {
1273                        Slog.e(TAG, "Unknown backup schedule version " + version);
1274                        return null;
1275                    }
1276
1277                    int N = in.readInt();
1278                    schedule = new ArrayList<FullBackupEntry>(N);
1279                    for (int i = 0; i < N; i++) {
1280                        String pkgName = in.readUTF();
1281                        long lastBackup = in.readLong();
1282                        schedule.add(new FullBackupEntry(pkgName, lastBackup));
1283                    }
1284                    Collections.sort(schedule);
1285                } catch (Exception e) {
1286                    Slog.e(TAG, "Unable to read backup schedule", e);
1287                    mFullBackupScheduleFile.delete();
1288                    schedule = null;
1289                } finally {
1290                    IoUtils.closeQuietly(in);
1291                    IoUtils.closeQuietly(bufStream);
1292                    IoUtils.closeQuietly(fstream);
1293                }
1294            }
1295
1296            if (schedule == null) {
1297                // no prior queue record, or unable to read it.  Set up the queue
1298                // from scratch.
1299                List<PackageInfo> apps =
1300                        PackageManagerBackupAgent.getStorableApplications(mPackageManager);
1301                final int N = apps.size();
1302                schedule = new ArrayList<FullBackupEntry>(N);
1303                for (int i = 0; i < N; i++) {
1304                    PackageInfo info = apps.get(i);
1305                    if (appGetsFullBackup(info)) {
1306                        schedule.add(new FullBackupEntry(info.packageName, 0));
1307                    }
1308                }
1309                writeFullBackupScheduleAsync();
1310            }
1311        }
1312        return schedule;
1313    }
1314
1315    Runnable mFullBackupScheduleWriter = new Runnable() {
1316        @Override public void run() {
1317            synchronized (mQueueLock) {
1318                try {
1319                    ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
1320                    DataOutputStream bufOut = new DataOutputStream(bufStream);
1321                    bufOut.writeInt(SCHEDULE_FILE_VERSION);
1322
1323                    // version 1:
1324                    //
1325                    // [int] # of packages in the queue = N
1326                    // N * {
1327                    //     [utf8] package name
1328                    //     [long] last backup time for this package
1329                    //     }
1330                    int N = mFullBackupQueue.size();
1331                    bufOut.writeInt(N);
1332
1333                    for (int i = 0; i < N; i++) {
1334                        FullBackupEntry entry = mFullBackupQueue.get(i);
1335                        bufOut.writeUTF(entry.packageName);
1336                        bufOut.writeLong(entry.lastBackup);
1337                    }
1338                    bufOut.flush();
1339
1340                    AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
1341                    FileOutputStream out = af.startWrite();
1342                    out.write(bufStream.toByteArray());
1343                    af.finishWrite(out);
1344                } catch (Exception e) {
1345                    Slog.e(TAG, "Unable to write backup schedule!", e);
1346                }
1347            }
1348        }
1349    };
1350
1351    private void writeFullBackupScheduleAsync() {
1352        mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
1353        mBackupHandler.post(mFullBackupScheduleWriter);
1354    }
1355
1356    private void parseLeftoverJournals() {
1357        for (File f : mJournalDir.listFiles()) {
1358            if (mJournal == null || f.compareTo(mJournal) != 0) {
1359                // This isn't the current journal, so it must be a leftover.  Read
1360                // out the package names mentioned there and schedule them for
1361                // backup.
1362                RandomAccessFile in = null;
1363                try {
1364                    Slog.i(TAG, "Found stale backup journal, scheduling");
1365                    in = new RandomAccessFile(f, "r");
1366                    while (true) {
1367                        String packageName = in.readUTF();
1368                        if (MORE_DEBUG) Slog.i(TAG, "  " + packageName);
1369                        dataChangedImpl(packageName);
1370                    }
1371                } catch (EOFException e) {
1372                    // no more data; we're done
1373                } catch (Exception e) {
1374                    Slog.e(TAG, "Can't read " + f, e);
1375                } finally {
1376                    // close/delete the file
1377                    try { if (in != null) in.close(); } catch (IOException e) {}
1378                    f.delete();
1379                }
1380            }
1381        }
1382    }
1383
1384    private SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) {
1385        return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds);
1386    }
1387
1388    private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) {
1389        try {
1390            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
1391            KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
1392            return keyFactory.generateSecret(ks);
1393        } catch (InvalidKeySpecException e) {
1394            Slog.e(TAG, "Invalid key spec for PBKDF2!");
1395        } catch (NoSuchAlgorithmException e) {
1396            Slog.e(TAG, "PBKDF2 unavailable!");
1397        }
1398        return null;
1399    }
1400
1401    private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) {
1402        SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds);
1403        if (key != null) {
1404            return byteArrayToHex(key.getEncoded());
1405        }
1406        return null;
1407    }
1408
1409    private String byteArrayToHex(byte[] data) {
1410        StringBuilder buf = new StringBuilder(data.length * 2);
1411        for (int i = 0; i < data.length; i++) {
1412            buf.append(Byte.toHexString(data[i], true));
1413        }
1414        return buf.toString();
1415    }
1416
1417    private byte[] hexToByteArray(String digits) {
1418        final int bytes = digits.length() / 2;
1419        if (2*bytes != digits.length()) {
1420            throw new IllegalArgumentException("Hex string must have an even number of digits");
1421        }
1422
1423        byte[] result = new byte[bytes];
1424        for (int i = 0; i < digits.length(); i += 2) {
1425            result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16);
1426        }
1427        return result;
1428    }
1429
1430    private byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) {
1431        char[] mkAsChar = new char[pwBytes.length];
1432        for (int i = 0; i < pwBytes.length; i++) {
1433            mkAsChar[i] = (char) pwBytes[i];
1434        }
1435
1436        Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds);
1437        return checksum.getEncoded();
1438    }
1439
1440    // Used for generating random salts or passwords
1441    private byte[] randomBytes(int bits) {
1442        byte[] array = new byte[bits / 8];
1443        mRng.nextBytes(array);
1444        return array;
1445    }
1446
1447    boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) {
1448        if (mPasswordHash == null) {
1449            // no current password case -- require that 'currentPw' be null or empty
1450            if (candidatePw == null || "".equals(candidatePw)) {
1451                return true;
1452            } // else the non-empty candidate does not match the empty stored pw
1453        } else {
1454            // hash the stated current pw and compare to the stored one
1455            if (candidatePw != null && candidatePw.length() > 0) {
1456                String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds);
1457                if (mPasswordHash.equalsIgnoreCase(currentPwHash)) {
1458                    // candidate hash matches the stored hash -- the password matches
1459                    return true;
1460                }
1461            } // else the stored pw is nonempty but the candidate is empty; no match
1462        }
1463        return false;
1464    }
1465
1466    public boolean setBackupPassword(String currentPw, String newPw) {
1467        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1468                "setBackupPassword");
1469
1470        // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes
1471        final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
1472
1473        // If the supplied pw doesn't hash to the the saved one, fail.  The password
1474        // might be caught in the legacy crypto mismatch; verify that too.
1475        if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
1476                && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
1477                        currentPw, PBKDF2_HASH_ROUNDS))) {
1478            return false;
1479        }
1480
1481        // Snap up to current on the pw file version
1482        mPasswordVersion = BACKUP_PW_FILE_VERSION;
1483        FileOutputStream pwFout = null;
1484        DataOutputStream pwOut = null;
1485        try {
1486            pwFout = new FileOutputStream(mPasswordVersionFile);
1487            pwOut = new DataOutputStream(pwFout);
1488            pwOut.writeInt(mPasswordVersion);
1489        } catch (IOException e) {
1490            Slog.e(TAG, "Unable to write backup pw version; password not changed");
1491            return false;
1492        } finally {
1493            try {
1494                if (pwOut != null) pwOut.close();
1495                if (pwFout != null) pwFout.close();
1496            } catch (IOException e) {
1497                Slog.w(TAG, "Unable to close pw version record");
1498            }
1499        }
1500
1501        // Clearing the password is okay
1502        if (newPw == null || newPw.isEmpty()) {
1503            if (mPasswordHashFile.exists()) {
1504                if (!mPasswordHashFile.delete()) {
1505                    // Unable to delete the old pw file, so fail
1506                    Slog.e(TAG, "Unable to clear backup password");
1507                    return false;
1508                }
1509            }
1510            mPasswordHash = null;
1511            mPasswordSalt = null;
1512            return true;
1513        }
1514
1515        try {
1516            // Okay, build the hash of the new backup password
1517            byte[] salt = randomBytes(PBKDF2_SALT_SIZE);
1518            String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS);
1519
1520            OutputStream pwf = null, buffer = null;
1521            DataOutputStream out = null;
1522            try {
1523                pwf = new FileOutputStream(mPasswordHashFile);
1524                buffer = new BufferedOutputStream(pwf);
1525                out = new DataOutputStream(buffer);
1526                // integer length of the salt array, followed by the salt,
1527                // then the hex pw hash string
1528                out.writeInt(salt.length);
1529                out.write(salt);
1530                out.writeUTF(newPwHash);
1531                out.flush();
1532                mPasswordHash = newPwHash;
1533                mPasswordSalt = salt;
1534                return true;
1535            } finally {
1536                if (out != null) out.close();
1537                if (buffer != null) buffer.close();
1538                if (pwf != null) pwf.close();
1539            }
1540        } catch (IOException e) {
1541            Slog.e(TAG, "Unable to set backup password");
1542        }
1543        return false;
1544    }
1545
1546    public boolean hasBackupPassword() {
1547        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1548                "hasBackupPassword");
1549
1550        return mPasswordHash != null && mPasswordHash.length() > 0;
1551    }
1552
1553    private boolean backupPasswordMatches(String currentPw) {
1554        if (hasBackupPassword()) {
1555            final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
1556            if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
1557                    && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
1558                            currentPw, PBKDF2_HASH_ROUNDS))) {
1559                if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
1560                return false;
1561            }
1562        }
1563        return true;
1564    }
1565
1566    // Maintain persistent state around whether need to do an initialize operation.
1567    // Must be called with the queue lock held.
1568    void recordInitPendingLocked(boolean isPending, String transportName) {
1569        if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
1570                + " on transport " + transportName);
1571        mBackupHandler.removeMessages(MSG_RETRY_INIT);
1572
1573        try {
1574            IBackupTransport transport = getTransport(transportName);
1575            if (transport != null) {
1576                String transportDirName = transport.transportDirName();
1577                File stateDir = new File(mBaseStateDir, transportDirName);
1578                File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1579
1580                if (isPending) {
1581                    // We need an init before we can proceed with sending backup data.
1582                    // Record that with an entry in our set of pending inits, as well as
1583                    // journaling it via creation of a sentinel file.
1584                    mPendingInits.add(transportName);
1585                    try {
1586                        (new FileOutputStream(initPendingFile)).close();
1587                    } catch (IOException ioe) {
1588                        // Something is badly wrong with our permissions; just try to move on
1589                    }
1590                } else {
1591                    // No more initialization needed; wipe the journal and reset our state.
1592                    initPendingFile.delete();
1593                    mPendingInits.remove(transportName);
1594                }
1595                return; // done; don't fall through to the error case
1596            }
1597        } catch (RemoteException e) {
1598            // transport threw when asked its name; fall through to the lookup-failed case
1599        }
1600
1601        // The named transport doesn't exist or threw.  This operation is
1602        // important, so we record the need for a an init and post a message
1603        // to retry the init later.
1604        if (isPending) {
1605            mPendingInits.add(transportName);
1606            mBackupHandler.sendMessageDelayed(
1607                    mBackupHandler.obtainMessage(MSG_RETRY_INIT,
1608                            (isPending ? 1 : 0),
1609                            0,
1610                            transportName),
1611                    TRANSPORT_RETRY_INTERVAL);
1612        }
1613    }
1614
1615    // Reset all of our bookkeeping, in response to having been told that
1616    // the backend data has been wiped [due to idle expiry, for example],
1617    // so we must re-upload all saved settings.
1618    void resetBackupState(File stateFileDir) {
1619        synchronized (mQueueLock) {
1620            // Wipe the "what we've ever backed up" tracking
1621            mEverStoredApps.clear();
1622            mEverStored.delete();
1623
1624            mCurrentToken = 0;
1625            writeRestoreTokens();
1626
1627            // Remove all the state files
1628            for (File sf : stateFileDir.listFiles()) {
1629                // ... but don't touch the needs-init sentinel
1630                if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
1631                    sf.delete();
1632                }
1633            }
1634        }
1635
1636        // Enqueue a new backup of every participant
1637        synchronized (mBackupParticipants) {
1638            final int N = mBackupParticipants.size();
1639            for (int i=0; i<N; i++) {
1640                HashSet<String> participants = mBackupParticipants.valueAt(i);
1641                if (participants != null) {
1642                    for (String packageName : participants) {
1643                        dataChangedImpl(packageName);
1644                    }
1645                }
1646            }
1647        }
1648    }
1649
1650    // Add a transport to our set of available backends.  If 'transport' is null, this
1651    // is an unregistration, and the transport's entry is removed from our bookkeeping.
1652    private void registerTransport(String name, String component, IBackupTransport transport) {
1653        synchronized (mTransports) {
1654            if (DEBUG) Slog.v(TAG, "Registering transport "
1655                    + component + "::" + name + " = " + transport);
1656            if (transport != null) {
1657                mTransports.put(name, transport);
1658                mTransportNames.put(component, name);
1659            } else {
1660                mTransports.remove(mTransportNames.get(component));
1661                mTransportNames.remove(component);
1662                // Nothing further to do in the unregistration case
1663                return;
1664            }
1665        }
1666
1667        // If the init sentinel file exists, we need to be sure to perform the init
1668        // as soon as practical.  We also create the state directory at registration
1669        // time to ensure it's present from the outset.
1670        try {
1671            String transportName = transport.transportDirName();
1672            File stateDir = new File(mBaseStateDir, transportName);
1673            stateDir.mkdirs();
1674
1675            File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1676            if (initSentinel.exists()) {
1677                synchronized (mQueueLock) {
1678                    mPendingInits.add(transportName);
1679
1680                    // TODO: pick a better starting time than now + 1 minute
1681                    long delay = 1000 * 60; // one minute, in milliseconds
1682                    mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1683                            System.currentTimeMillis() + delay, mRunInitIntent);
1684                }
1685            }
1686        } catch (RemoteException e) {
1687            // the transport threw when asked its file naming prefs; declare it invalid
1688            Slog.e(TAG, "Unable to register transport as " + name);
1689            mTransportNames.remove(component);
1690            mTransports.remove(name);
1691        }
1692    }
1693
1694    // ----- Track installation/removal of packages -----
1695    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1696        public void onReceive(Context context, Intent intent) {
1697            if (DEBUG) Slog.d(TAG, "Received broadcast " + intent);
1698
1699            String action = intent.getAction();
1700            boolean replacing = false;
1701            boolean added = false;
1702            boolean changed = false;
1703            Bundle extras = intent.getExtras();
1704            String pkgList[] = null;
1705            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
1706                    Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
1707                    Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
1708                Uri uri = intent.getData();
1709                if (uri == null) {
1710                    return;
1711                }
1712                String pkgName = uri.getSchemeSpecificPart();
1713                if (pkgName != null) {
1714                    pkgList = new String[] { pkgName };
1715                }
1716                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
1717
1718                // At package-changed we only care about looking at new transport states
1719                if (changed) {
1720                    try {
1721                        if (MORE_DEBUG) {
1722                            Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
1723                        }
1724                        // unbind existing possibly-stale connections to that package's transports
1725                        synchronized (mTransports) {
1726                            TransportConnection conn = mTransportConnections.get(pkgName);
1727                            if (conn != null) {
1728                                final ServiceInfo svc = conn.mTransport;
1729                                ComponentName svcName =
1730                                        new ComponentName(svc.packageName, svc.name);
1731                                String flatName = svcName.flattenToShortString();
1732                                Slog.i(TAG, "Unbinding " + svcName);
1733
1734                                mContext.unbindService(conn);
1735                                mTransportConnections.remove(pkgName);
1736                                mTransports.remove(mTransportNames.get(flatName));
1737                                mTransportNames.remove(flatName);
1738                            }
1739                        }
1740                        // and then (re)bind as appropriate
1741                        PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0);
1742                        checkForTransportAndBind(app);
1743                    } catch (NameNotFoundException e) {
1744                        // Nope, can't find it - just ignore
1745                        if (MORE_DEBUG) {
1746                            Slog.w(TAG, "Can't find changed package " + pkgName);
1747                        }
1748                    }
1749                    return; // nothing more to do in the PACKAGE_CHANGED case
1750                }
1751
1752                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1753                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
1754            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
1755                added = true;
1756                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1757            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
1758                added = false;
1759                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1760            }
1761
1762            if (pkgList == null || pkgList.length == 0) {
1763                return;
1764            }
1765
1766            final int uid = extras.getInt(Intent.EXTRA_UID);
1767            if (added) {
1768                synchronized (mBackupParticipants) {
1769                    if (replacing) {
1770                        // This is the package-replaced case; we just remove the entry
1771                        // under the old uid and fall through to re-add.
1772                        removePackageParticipantsLocked(pkgList, uid);
1773                    }
1774                    addPackageParticipantsLocked(pkgList);
1775                }
1776                // If they're full-backup candidates, add them there instead
1777                for (String packageName : pkgList) {
1778                    try {
1779                        PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
1780                        long now = System.currentTimeMillis();
1781                        if (appGetsFullBackup(app)) {
1782                            enqueueFullBackup(packageName, now);
1783                            scheduleNextFullBackupJob();
1784                        }
1785
1786                        // Transport maintenance: rebind to known existing transports that have
1787                        // just been updated; and bind to any newly-installed transport services.
1788                        synchronized (mTransports) {
1789                            final TransportConnection conn = mTransportConnections.get(packageName);
1790                            if (conn != null) {
1791                                if (MORE_DEBUG) {
1792                                    Slog.i(TAG, "Transport package changed; rebinding");
1793                                }
1794                                bindTransport(conn.mTransport);
1795                            } else {
1796                                checkForTransportAndBind(app);
1797                            }
1798                        }
1799
1800                    } catch (NameNotFoundException e) {
1801                        // doesn't really exist; ignore it
1802                        if (DEBUG) {
1803                            Slog.i(TAG, "Can't resolve new app " + packageName);
1804                        }
1805                    }
1806                }
1807
1808            } else {
1809                if (replacing) {
1810                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
1811                } else {
1812                    synchronized (mBackupParticipants) {
1813                        removePackageParticipantsLocked(pkgList, uid);
1814                    }
1815                }
1816            }
1817        }
1818    };
1819
1820    // ----- Track connection to transports service -----
1821    class TransportConnection implements ServiceConnection {
1822        ServiceInfo mTransport;
1823
1824        public TransportConnection(ServiceInfo transport) {
1825            mTransport = transport;
1826        }
1827
1828        @Override
1829        public void onServiceConnected(ComponentName component, IBinder service) {
1830            if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
1831            final String name = component.flattenToShortString();
1832            try {
1833                IBackupTransport transport = IBackupTransport.Stub.asInterface(service);
1834                registerTransport(transport.name(), name, transport);
1835                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 1);
1836            } catch (RemoteException e) {
1837                Slog.e(TAG, "Unable to register transport " + component);
1838                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
1839            }
1840        }
1841
1842        @Override
1843        public void onServiceDisconnected(ComponentName component) {
1844            if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component);
1845            final String name = component.flattenToShortString();
1846            EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
1847            registerTransport(null, name, null);
1848        }
1849    };
1850
1851    // Check whether the given package hosts a transport, and bind if so
1852    void checkForTransportAndBind(PackageInfo pkgInfo) {
1853        Intent intent = new Intent(mTransportServiceIntent)
1854                .setPackage(pkgInfo.packageName);
1855        List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
1856                intent, 0, UserHandle.USER_OWNER);
1857        final int N = hosts.size();
1858        for (int i = 0; i < N; i++) {
1859            final ServiceInfo info = hosts.get(i).serviceInfo;
1860            tryBindTransport(info);
1861        }
1862    }
1863
1864    // Verify that the service exists and is hosted by a privileged app, then proceed to bind
1865    boolean tryBindTransport(ServiceInfo info) {
1866        try {
1867            PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
1868            if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
1869                    != 0) {
1870                return bindTransport(info);
1871            } else {
1872                Slog.w(TAG, "Transport package " + info.packageName + " not privileged");
1873            }
1874        } catch (NameNotFoundException e) {
1875            Slog.w(TAG, "Problem resolving transport package " + info.packageName);
1876        }
1877        return false;
1878    }
1879
1880    // Actually bind; presumes that we have already validated the transport service
1881    boolean bindTransport(ServiceInfo transport) {
1882        ComponentName svcName = new ComponentName(transport.packageName, transport.name);
1883        if (DEBUG) {
1884            Slog.i(TAG, "Binding to transport host " + svcName);
1885        }
1886        Intent intent = new Intent(mTransportServiceIntent);
1887        intent.setComponent(svcName);
1888
1889        TransportConnection connection;
1890        synchronized (mTransports) {
1891            connection = mTransportConnections.get(transport.packageName);
1892            if (null == connection) {
1893                connection = new TransportConnection(transport);
1894                mTransportConnections.put(transport.packageName, connection);
1895            } else {
1896                // This is a rebind due to package upgrade.  The service won't be
1897                // automatically relaunched for us until we explicitly rebind, but
1898                // we need to unbind the now-orphaned original connection.
1899                mContext.unbindService(connection);
1900            }
1901        }
1902        return mContext.bindServiceAsUser(intent,
1903                connection, Context.BIND_AUTO_CREATE,
1904                UserHandle.OWNER);
1905    }
1906
1907    // Add the backup agents in the given packages to our set of known backup participants.
1908    // If 'packageNames' is null, adds all backup agents in the whole system.
1909    void addPackageParticipantsLocked(String[] packageNames) {
1910        // Look for apps that define the android:backupAgent attribute
1911        List<PackageInfo> targetApps = allAgentPackages();
1912        if (packageNames != null) {
1913            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
1914            for (String packageName : packageNames) {
1915                addPackageParticipantsLockedInner(packageName, targetApps);
1916            }
1917        } else {
1918            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
1919            addPackageParticipantsLockedInner(null, targetApps);
1920        }
1921    }
1922
1923    private void addPackageParticipantsLockedInner(String packageName,
1924            List<PackageInfo> targetPkgs) {
1925        if (MORE_DEBUG) {
1926            Slog.v(TAG, "Examining " + packageName + " for backup agent");
1927        }
1928
1929        for (PackageInfo pkg : targetPkgs) {
1930            if (packageName == null || pkg.packageName.equals(packageName)) {
1931                int uid = pkg.applicationInfo.uid;
1932                HashSet<String> set = mBackupParticipants.get(uid);
1933                if (set == null) {
1934                    set = new HashSet<String>();
1935                    mBackupParticipants.put(uid, set);
1936                }
1937                set.add(pkg.packageName);
1938                if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
1939
1940                // Schedule a backup for it on general principles
1941                if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
1942                dataChangedImpl(pkg.packageName);
1943            }
1944        }
1945    }
1946
1947    // Remove the given packages' entries from our known active set.
1948    void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
1949        if (packageNames == null) {
1950            Slog.w(TAG, "removePackageParticipants with null list");
1951            return;
1952        }
1953
1954        if (MORE_DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
1955                + " #" + packageNames.length);
1956        for (String pkg : packageNames) {
1957            // Known previous UID, so we know which package set to check
1958            HashSet<String> set = mBackupParticipants.get(oldUid);
1959            if (set != null && set.contains(pkg)) {
1960                removePackageFromSetLocked(set, pkg);
1961                if (set.isEmpty()) {
1962                    if (MORE_DEBUG) Slog.v(TAG, "  last one of this uid; purging set");
1963                    mBackupParticipants.remove(oldUid);
1964                }
1965            }
1966        }
1967    }
1968
1969    private void removePackageFromSetLocked(final HashSet<String> set,
1970            final String packageName) {
1971        if (set.contains(packageName)) {
1972            // Found it.  Remove this one package from the bookkeeping, and
1973            // if it's the last participating app under this uid we drop the
1974            // (now-empty) set as well.
1975            // Note that we deliberately leave it 'known' in the "ever backed up"
1976            // bookkeeping so that its current-dataset data will be retrieved
1977            // if the app is subsequently reinstalled
1978            if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
1979            set.remove(packageName);
1980            mPendingBackups.remove(packageName);
1981        }
1982    }
1983
1984    // Returns the set of all applications that define an android:backupAgent attribute
1985    List<PackageInfo> allAgentPackages() {
1986        // !!! TODO: cache this and regenerate only when necessary
1987        int flags = PackageManager.GET_SIGNATURES;
1988        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
1989        int N = packages.size();
1990        for (int a = N-1; a >= 0; a--) {
1991            PackageInfo pkg = packages.get(a);
1992            try {
1993                ApplicationInfo app = pkg.applicationInfo;
1994                if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
1995                        || app.backupAgentName == null) {
1996                    packages.remove(a);
1997                }
1998                else {
1999                    // we will need the shared library path, so look that up and store it here.
2000                    // This is used implicitly when we pass the PackageInfo object off to
2001                    // the Activity Manager to launch the app for backup/restore purposes.
2002                    app = mPackageManager.getApplicationInfo(pkg.packageName,
2003                            PackageManager.GET_SHARED_LIBRARY_FILES);
2004                    pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
2005                }
2006            } catch (NameNotFoundException e) {
2007                packages.remove(a);
2008            }
2009        }
2010        return packages;
2011    }
2012
2013    // Called from the backup tasks: record that the given app has been successfully
2014    // backed up at least once.  This includes both key/value and full-data backups
2015    // through the transport.
2016    void logBackupComplete(String packageName) {
2017        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
2018
2019        synchronized (mEverStoredApps) {
2020            if (!mEverStoredApps.add(packageName)) return;
2021
2022            RandomAccessFile out = null;
2023            try {
2024                out = new RandomAccessFile(mEverStored, "rws");
2025                out.seek(out.length());
2026                out.writeUTF(packageName);
2027            } catch (IOException e) {
2028                Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
2029            } finally {
2030                try { if (out != null) out.close(); } catch (IOException e) {}
2031            }
2032        }
2033    }
2034
2035    // Remove our awareness of having ever backed up the given package
2036    void removeEverBackedUp(String packageName) {
2037        if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
2038        if (MORE_DEBUG) Slog.v(TAG, "New set:");
2039
2040        synchronized (mEverStoredApps) {
2041            // Rewrite the file and rename to overwrite.  If we reboot in the middle,
2042            // we'll recognize on initialization time that the package no longer
2043            // exists and fix it up then.
2044            File tempKnownFile = new File(mBaseStateDir, "processed.new");
2045            RandomAccessFile known = null;
2046            try {
2047                known = new RandomAccessFile(tempKnownFile, "rws");
2048                mEverStoredApps.remove(packageName);
2049                for (String s : mEverStoredApps) {
2050                    known.writeUTF(s);
2051                    if (MORE_DEBUG) Slog.v(TAG, "    " + s);
2052                }
2053                known.close();
2054                known = null;
2055                if (!tempKnownFile.renameTo(mEverStored)) {
2056                    throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
2057                }
2058            } catch (IOException e) {
2059                // Bad: we couldn't create the new copy.  For safety's sake we
2060                // abandon the whole process and remove all what's-backed-up
2061                // state entirely, meaning we'll force a backup pass for every
2062                // participant on the next boot or [re]install.
2063                Slog.w(TAG, "Error rewriting " + mEverStored, e);
2064                mEverStoredApps.clear();
2065                tempKnownFile.delete();
2066                mEverStored.delete();
2067            } finally {
2068                try { if (known != null) known.close(); } catch (IOException e) {}
2069            }
2070        }
2071    }
2072
2073    // Persistently record the current and ancestral backup tokens as well
2074    // as the set of packages with data [supposedly] available in the
2075    // ancestral dataset.
2076    void writeRestoreTokens() {
2077        try {
2078            RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
2079
2080            // First, the version number of this record, for futureproofing
2081            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
2082
2083            // Write the ancestral and current tokens
2084            af.writeLong(mAncestralToken);
2085            af.writeLong(mCurrentToken);
2086
2087            // Now write the set of ancestral packages
2088            if (mAncestralPackages == null) {
2089                af.writeInt(-1);
2090            } else {
2091                af.writeInt(mAncestralPackages.size());
2092                if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
2093                for (String pkgName : mAncestralPackages) {
2094                    af.writeUTF(pkgName);
2095                    if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
2096                }
2097            }
2098            af.close();
2099        } catch (IOException e) {
2100            Slog.w(TAG, "Unable to write token file:", e);
2101        }
2102    }
2103
2104    // Return the given transport
2105    private IBackupTransport getTransport(String transportName) {
2106        synchronized (mTransports) {
2107            IBackupTransport transport = mTransports.get(transportName);
2108            if (transport == null) {
2109                Slog.w(TAG, "Requested unavailable transport: " + transportName);
2110            }
2111            return transport;
2112        }
2113    }
2114
2115    // fire off a backup agent, blocking until it attaches or times out
2116    IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
2117        IBackupAgent agent = null;
2118        synchronized(mAgentConnectLock) {
2119            mConnecting = true;
2120            mConnectedAgent = null;
2121            try {
2122                if (mActivityManager.bindBackupAgent(app.packageName, mode,
2123                        UserHandle.USER_OWNER)) {
2124                    Slog.d(TAG, "awaiting agent for " + app);
2125
2126                    // success; wait for the agent to arrive
2127                    // only wait 10 seconds for the bind to happen
2128                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
2129                    while (mConnecting && mConnectedAgent == null
2130                            && (System.currentTimeMillis() < timeoutMark)) {
2131                        try {
2132                            mAgentConnectLock.wait(5000);
2133                        } catch (InterruptedException e) {
2134                            // just bail
2135                            if (DEBUG) Slog.w(TAG, "Interrupted: " + e);
2136                            mActivityManager.clearPendingBackup();
2137                            return null;
2138                        }
2139                    }
2140
2141                    // if we timed out with no connect, abort and move on
2142                    if (mConnecting == true) {
2143                        Slog.w(TAG, "Timeout waiting for agent " + app);
2144                        mActivityManager.clearPendingBackup();
2145                        return null;
2146                    }
2147                    if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
2148                    agent = mConnectedAgent;
2149                }
2150            } catch (RemoteException e) {
2151                // can't happen - ActivityManager is local
2152            }
2153        }
2154        return agent;
2155    }
2156
2157    // clear an application's data, blocking until the operation completes or times out
2158    void clearApplicationDataSynchronous(String packageName) {
2159        // Don't wipe packages marked allowClearUserData=false
2160        try {
2161            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
2162            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
2163                if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
2164                        + packageName);
2165                return;
2166            }
2167        } catch (NameNotFoundException e) {
2168            Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
2169            return;
2170        }
2171
2172        ClearDataObserver observer = new ClearDataObserver();
2173
2174        synchronized(mClearDataLock) {
2175            mClearingData = true;
2176            try {
2177                mActivityManager.clearApplicationUserData(packageName, observer, 0);
2178            } catch (RemoteException e) {
2179                // can't happen because the activity manager is in this process
2180            }
2181
2182            // only wait 10 seconds for the clear data to happen
2183            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
2184            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
2185                try {
2186                    mClearDataLock.wait(5000);
2187                } catch (InterruptedException e) {
2188                    // won't happen, but still.
2189                    mClearingData = false;
2190                }
2191            }
2192        }
2193    }
2194
2195    class ClearDataObserver extends IPackageDataObserver.Stub {
2196        public void onRemoveCompleted(String packageName, boolean succeeded) {
2197            synchronized(mClearDataLock) {
2198                mClearingData = false;
2199                mClearDataLock.notifyAll();
2200            }
2201        }
2202    }
2203
2204    // Get the restore-set token for the best-available restore set for this package:
2205    // the active set if possible, else the ancestral one.  Returns zero if none available.
2206    long getAvailableRestoreToken(String packageName) {
2207        long token = mAncestralToken;
2208        synchronized (mQueueLock) {
2209            if (mEverStoredApps.contains(packageName)) {
2210                token = mCurrentToken;
2211            }
2212        }
2213        return token;
2214    }
2215
2216    // -----
2217    // Interface and methods used by the asynchronous-with-timeout backup/restore operations
2218
2219    interface BackupRestoreTask {
2220        // Execute one tick of whatever state machine the task implements
2221        void execute();
2222
2223        // An operation that wanted a callback has completed
2224        void operationComplete();
2225
2226        // An operation that wanted a callback has timed out
2227        void handleTimeout();
2228    }
2229
2230    void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) {
2231        if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
2232                + " interval=" + interval);
2233        synchronized (mCurrentOpLock) {
2234            mCurrentOperations.put(token, new Operation(OP_PENDING, callback));
2235
2236            Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback);
2237            mBackupHandler.sendMessageDelayed(msg, interval);
2238        }
2239    }
2240
2241    // synchronous waiter case
2242    boolean waitUntilOperationComplete(int token) {
2243        if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
2244                + Integer.toHexString(token));
2245        int finalState = OP_PENDING;
2246        Operation op = null;
2247        synchronized (mCurrentOpLock) {
2248            while (true) {
2249                op = mCurrentOperations.get(token);
2250                if (op == null) {
2251                    // mysterious disappearance: treat as success with no callback
2252                    break;
2253                } else {
2254                    if (op.state == OP_PENDING) {
2255                        try {
2256                            mCurrentOpLock.wait();
2257                        } catch (InterruptedException e) {}
2258                        // When the wait is notified we loop around and recheck the current state
2259                    } else {
2260                        // No longer pending; we're done
2261                        finalState = op.state;
2262                        break;
2263                    }
2264                }
2265            }
2266        }
2267
2268        mBackupHandler.removeMessages(MSG_TIMEOUT);
2269        if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
2270                + " complete: finalState=" + finalState);
2271        return finalState == OP_ACKNOWLEDGED;
2272    }
2273
2274    void handleTimeout(int token, Object obj) {
2275        // Notify any synchronous waiters
2276        Operation op = null;
2277        synchronized (mCurrentOpLock) {
2278            op = mCurrentOperations.get(token);
2279            if (MORE_DEBUG) {
2280                if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token)
2281                        + " but no op found");
2282            }
2283            int state = (op != null) ? op.state : OP_TIMEOUT;
2284            if (state == OP_PENDING) {
2285                if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token));
2286                op.state = OP_TIMEOUT;
2287                mCurrentOperations.put(token, op);
2288            }
2289            mCurrentOpLock.notifyAll();
2290        }
2291
2292        // If there's a TimeoutHandler for this event, call it
2293        if (op != null && op.callback != null) {
2294            op.callback.handleTimeout();
2295        }
2296    }
2297
2298    // ----- Back up a set of applications via a worker thread -----
2299
2300    enum BackupState {
2301        INITIAL,
2302        RUNNING_QUEUE,
2303        FINAL
2304    }
2305
2306    class PerformBackupTask implements BackupRestoreTask {
2307        private static final String TAG = "PerformBackupTask";
2308
2309        IBackupTransport mTransport;
2310        ArrayList<BackupRequest> mQueue;
2311        ArrayList<BackupRequest> mOriginalQueue;
2312        File mStateDir;
2313        File mJournal;
2314        BackupState mCurrentState;
2315
2316        // carried information about the current in-flight operation
2317        IBackupAgent mAgentBinder;
2318        PackageInfo mCurrentPackage;
2319        File mSavedStateName;
2320        File mBackupDataName;
2321        File mNewStateName;
2322        ParcelFileDescriptor mSavedState;
2323        ParcelFileDescriptor mBackupData;
2324        ParcelFileDescriptor mNewState;
2325        int mStatus;
2326        boolean mFinished;
2327
2328        public PerformBackupTask(IBackupTransport transport, String dirName,
2329                ArrayList<BackupRequest> queue, File journal) {
2330            mTransport = transport;
2331            mOriginalQueue = queue;
2332            mJournal = journal;
2333
2334            mStateDir = new File(mBaseStateDir, dirName);
2335
2336            mCurrentState = BackupState.INITIAL;
2337            mFinished = false;
2338
2339            addBackupTrace("STATE => INITIAL");
2340        }
2341
2342        // Main entry point: perform one chunk of work, updating the state as appropriate
2343        // and reposting the next chunk to the primary backup handler thread.
2344        @Override
2345        public void execute() {
2346            switch (mCurrentState) {
2347                case INITIAL:
2348                    beginBackup();
2349                    break;
2350
2351                case RUNNING_QUEUE:
2352                    invokeNextAgent();
2353                    break;
2354
2355                case FINAL:
2356                    if (!mFinished) finalizeBackup();
2357                    else {
2358                        Slog.e(TAG, "Duplicate finish");
2359                    }
2360                    mFinished = true;
2361                    break;
2362            }
2363        }
2364
2365        // We're starting a backup pass.  Initialize the transport and send
2366        // the PM metadata blob if we haven't already.
2367        void beginBackup() {
2368            if (DEBUG_BACKUP_TRACE) {
2369                clearBackupTrace();
2370                StringBuilder b = new StringBuilder(256);
2371                b.append("beginBackup: [");
2372                for (BackupRequest req : mOriginalQueue) {
2373                    b.append(' ');
2374                    b.append(req.packageName);
2375                }
2376                b.append(" ]");
2377                addBackupTrace(b.toString());
2378            }
2379
2380            mAgentBinder = null;
2381            mStatus = BackupTransport.TRANSPORT_OK;
2382
2383            // Sanity check: if the queue is empty we have no work to do.
2384            if (mOriginalQueue.isEmpty()) {
2385                Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
2386                addBackupTrace("queue empty at begin");
2387                executeNextState(BackupState.FINAL);
2388                return;
2389            }
2390
2391            // We need to retain the original queue contents in case of transport
2392            // failure, but we want a working copy that we can manipulate along
2393            // the way.
2394            mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
2395
2396            if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
2397
2398            File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2399            try {
2400                final String transportName = mTransport.transportDirName();
2401                EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
2402
2403                // If we haven't stored package manager metadata yet, we must init the transport.
2404                if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
2405                    Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
2406                    addBackupTrace("initializing transport " + transportName);
2407                    resetBackupState(mStateDir);  // Just to make sure.
2408                    mStatus = mTransport.initializeDevice();
2409
2410                    addBackupTrace("transport.initializeDevice() == " + mStatus);
2411                    if (mStatus == BackupTransport.TRANSPORT_OK) {
2412                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
2413                    } else {
2414                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
2415                        Slog.e(TAG, "Transport error in initializeDevice()");
2416                    }
2417                }
2418
2419                // The package manager doesn't have a proper <application> etc, but since
2420                // it's running here in the system process we can just set up its agent
2421                // directly and use a synthetic BackupRequest.  We always run this pass
2422                // because it's cheap and this way we guarantee that we don't get out of
2423                // step even if we're selecting among various transports at run time.
2424                if (mStatus == BackupTransport.TRANSPORT_OK) {
2425                    PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
2426                            mPackageManager);
2427                    mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
2428                            IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
2429                    addBackupTrace("PMBA invoke: " + mStatus);
2430
2431                    // Because the PMBA is a local instance, it has already executed its
2432                    // backup callback and returned.  Blow away the lingering (spurious)
2433                    // pending timeout message for it.
2434                    mBackupHandler.removeMessages(MSG_TIMEOUT);
2435                }
2436
2437                if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
2438                    // The backend reports that our dataset has been wiped.  Note this in
2439                    // the event log; the no-success code below will reset the backup
2440                    // state as well.
2441                    EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
2442                }
2443            } catch (Exception e) {
2444                Slog.e(TAG, "Error in backup thread", e);
2445                addBackupTrace("Exception in backup thread: " + e);
2446                mStatus = BackupTransport.TRANSPORT_ERROR;
2447            } finally {
2448                // If we've succeeded so far, invokeAgentForBackup() will have run the PM
2449                // metadata and its completion/timeout callback will continue the state
2450                // machine chain.  If it failed that won't happen; we handle that now.
2451                addBackupTrace("exiting prelim: " + mStatus);
2452                if (mStatus != BackupTransport.TRANSPORT_OK) {
2453                    // if things went wrong at this point, we need to
2454                    // restage everything and try again later.
2455                    resetBackupState(mStateDir);  // Just to make sure.
2456                    executeNextState(BackupState.FINAL);
2457                }
2458            }
2459        }
2460
2461        // Transport has been initialized and the PM metadata submitted successfully
2462        // if that was warranted.  Now we process the single next thing in the queue.
2463        void invokeNextAgent() {
2464            mStatus = BackupTransport.TRANSPORT_OK;
2465            addBackupTrace("invoke q=" + mQueue.size());
2466
2467            // Sanity check that we have work to do.  If not, skip to the end where
2468            // we reestablish the wakelock invariants etc.
2469            if (mQueue.isEmpty()) {
2470                if (DEBUG) Slog.i(TAG, "queue now empty");
2471                executeNextState(BackupState.FINAL);
2472                return;
2473            }
2474
2475            // pop the entry we're going to process on this step
2476            BackupRequest request = mQueue.get(0);
2477            mQueue.remove(0);
2478
2479            Slog.d(TAG, "starting agent for backup of " + request);
2480            addBackupTrace("launch agent for " + request.packageName);
2481
2482            // Verify that the requested app exists; it might be something that
2483            // requested a backup but was then uninstalled.  The request was
2484            // journalled and rather than tamper with the journal it's safer
2485            // to sanity-check here.  This also gives us the classname of the
2486            // package's backup agent.
2487            try {
2488                mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
2489                        PackageManager.GET_SIGNATURES);
2490                if (mCurrentPackage.applicationInfo.backupAgentName == null) {
2491                    // The manifest has changed but we had a stale backup request pending.
2492                    // This won't happen again because the app won't be requesting further
2493                    // backups.
2494                    Slog.i(TAG, "Package " + request.packageName
2495                            + " no longer supports backup; skipping");
2496                    addBackupTrace("skipping - no agent, completion is noop");
2497                    executeNextState(BackupState.RUNNING_QUEUE);
2498                    return;
2499                }
2500
2501                if ((mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
2502                    // The app has been force-stopped or cleared or just installed,
2503                    // and not yet launched out of that state, so just as it won't
2504                    // receive broadcasts, we won't run it for backup.
2505                    addBackupTrace("skipping - stopped");
2506                    executeNextState(BackupState.RUNNING_QUEUE);
2507                    return;
2508                }
2509
2510                IBackupAgent agent = null;
2511                try {
2512                    mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
2513                    agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
2514                            IApplicationThread.BACKUP_MODE_INCREMENTAL);
2515                    addBackupTrace("agent bound; a? = " + (agent != null));
2516                    if (agent != null) {
2517                        mAgentBinder = agent;
2518                        mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
2519                        // at this point we'll either get a completion callback from the
2520                        // agent, or a timeout message on the main handler.  either way, we're
2521                        // done here as long as we're successful so far.
2522                    } else {
2523                        // Timeout waiting for the agent
2524                        mStatus = BackupTransport.AGENT_ERROR;
2525                    }
2526                } catch (SecurityException ex) {
2527                    // Try for the next one.
2528                    Slog.d(TAG, "error in bind/backup", ex);
2529                    mStatus = BackupTransport.AGENT_ERROR;
2530                            addBackupTrace("agent SE");
2531                }
2532            } catch (NameNotFoundException e) {
2533                Slog.d(TAG, "Package does not exist; skipping");
2534                addBackupTrace("no such package");
2535                mStatus = BackupTransport.AGENT_UNKNOWN;
2536            } finally {
2537                mWakelock.setWorkSource(null);
2538
2539                // If there was an agent error, no timeout/completion handling will occur.
2540                // That means we need to direct to the next state ourselves.
2541                if (mStatus != BackupTransport.TRANSPORT_OK) {
2542                    BackupState nextState = BackupState.RUNNING_QUEUE;
2543                    mAgentBinder = null;
2544
2545                    // An agent-level failure means we reenqueue this one agent for
2546                    // a later retry, but otherwise proceed normally.
2547                    if (mStatus == BackupTransport.AGENT_ERROR) {
2548                        if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
2549                                + " - restaging");
2550                        dataChangedImpl(request.packageName);
2551                        mStatus = BackupTransport.TRANSPORT_OK;
2552                        if (mQueue.isEmpty()) nextState = BackupState.FINAL;
2553                    } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
2554                        // Failed lookup of the app, so we couldn't bring up an agent, but
2555                        // we're otherwise fine.  Just drop it and go on to the next as usual.
2556                        mStatus = BackupTransport.TRANSPORT_OK;
2557                    } else {
2558                        // Transport-level failure means we reenqueue everything
2559                        revertAndEndBackup();
2560                        nextState = BackupState.FINAL;
2561                    }
2562
2563                    executeNextState(nextState);
2564                } else {
2565                    // success case
2566                    addBackupTrace("expecting completion/timeout callback");
2567                }
2568            }
2569        }
2570
2571        void finalizeBackup() {
2572            addBackupTrace("finishing");
2573
2574            // Either backup was successful, in which case we of course do not need
2575            // this pass's journal any more; or it failed, in which case we just
2576            // re-enqueued all of these packages in the current active journal.
2577            // Either way, we no longer need this pass's journal.
2578            if (mJournal != null && !mJournal.delete()) {
2579                Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
2580            }
2581
2582            // If everything actually went through and this is the first time we've
2583            // done a backup, we can now record what the current backup dataset token
2584            // is.
2585            if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) {
2586                addBackupTrace("success; recording token");
2587                try {
2588                    mCurrentToken = mTransport.getCurrentRestoreSet();
2589                    writeRestoreTokens();
2590                } catch (RemoteException e) {
2591                    // nothing for it at this point, unfortunately, but this will be
2592                    // recorded the next time we fully succeed.
2593                    addBackupTrace("transport threw returning token");
2594                }
2595            }
2596
2597            // Set up the next backup pass - at this point we can set mBackupRunning
2598            // to false to allow another pass to fire, because we're done with the
2599            // state machine sequence and the wakelock is refcounted.
2600            synchronized (mQueueLock) {
2601                mBackupRunning = false;
2602                if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
2603                    // Make sure we back up everything and perform the one-time init
2604                    clearMetadata();
2605                    if (DEBUG) Slog.d(TAG, "Server requires init; rerunning");
2606                    addBackupTrace("init required; rerunning");
2607                    backupNow();
2608                }
2609            }
2610
2611            // Only once we're entirely finished do we release the wakelock
2612            clearBackupTrace();
2613            Slog.i(BackupManagerService.TAG, "Backup pass finished.");
2614            mWakelock.release();
2615        }
2616
2617        // Remove the PM metadata state. This will generate an init on the next pass.
2618        void clearMetadata() {
2619            final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2620            if (pmState.exists()) pmState.delete();
2621        }
2622
2623        // Invoke an agent's doBackup() and start a timeout message spinning on the main
2624        // handler in case it doesn't get back to us.
2625        int invokeAgentForBackup(String packageName, IBackupAgent agent,
2626                IBackupTransport transport) {
2627            if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName);
2628            addBackupTrace("invoking " + packageName);
2629
2630            mSavedStateName = new File(mStateDir, packageName);
2631            mBackupDataName = new File(mDataDir, packageName + ".data");
2632            mNewStateName = new File(mStateDir, packageName + ".new");
2633            if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName);
2634
2635            mSavedState = null;
2636            mBackupData = null;
2637            mNewState = null;
2638
2639            final int token = generateToken();
2640            try {
2641                // Look up the package info & signatures.  This is first so that if it
2642                // throws an exception, there's no file setup yet that would need to
2643                // be unraveled.
2644                if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
2645                    // The metadata 'package' is synthetic; construct one and make
2646                    // sure our global state is pointed at it
2647                    mCurrentPackage = new PackageInfo();
2648                    mCurrentPackage.packageName = packageName;
2649                }
2650
2651                // In a full backup, we pass a null ParcelFileDescriptor as
2652                // the saved-state "file". This is by definition an incremental,
2653                // so we build a saved state file to pass.
2654                mSavedState = ParcelFileDescriptor.open(mSavedStateName,
2655                        ParcelFileDescriptor.MODE_READ_ONLY |
2656                        ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
2657
2658                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
2659                        ParcelFileDescriptor.MODE_READ_WRITE |
2660                        ParcelFileDescriptor.MODE_CREATE |
2661                        ParcelFileDescriptor.MODE_TRUNCATE);
2662
2663                if (!SELinux.restorecon(mBackupDataName)) {
2664                    Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
2665                }
2666
2667                mNewState = ParcelFileDescriptor.open(mNewStateName,
2668                        ParcelFileDescriptor.MODE_READ_WRITE |
2669                        ParcelFileDescriptor.MODE_CREATE |
2670                        ParcelFileDescriptor.MODE_TRUNCATE);
2671
2672                // Initiate the target's backup pass
2673                addBackupTrace("setting timeout");
2674                prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
2675                addBackupTrace("calling agent doBackup()");
2676                agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder);
2677            } catch (Exception e) {
2678                Slog.e(TAG, "Error invoking for backup on " + packageName);
2679                addBackupTrace("exception: " + e);
2680                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
2681                        e.toString());
2682                agentErrorCleanup();
2683                return BackupTransport.AGENT_ERROR;
2684            }
2685
2686            // At this point the agent is off and running.  The next thing to happen will
2687            // either be a callback from the agent, at which point we'll process its data
2688            // for transport, or a timeout.  Either way the next phase will happen in
2689            // response to the TimeoutHandler interface callbacks.
2690            addBackupTrace("invoke success");
2691            return BackupTransport.TRANSPORT_OK;
2692        }
2693
2694        public void failAgent(IBackupAgent agent, String message) {
2695            try {
2696                agent.fail(message);
2697            } catch (Exception e) {
2698                Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName);
2699            }
2700        }
2701
2702        // SHA-1 a byte array and return the result in hex
2703        private String SHA1Checksum(byte[] input) {
2704            final byte[] checksum;
2705            try {
2706                MessageDigest md = MessageDigest.getInstance("SHA-1");
2707                checksum = md.digest(input);
2708            } catch (NoSuchAlgorithmException e) {
2709                Slog.e(TAG, "Unable to use SHA-1!");
2710                return "00";
2711            }
2712
2713            StringBuffer sb = new StringBuffer(checksum.length * 2);
2714            for (int i = 0; i < checksum.length; i++) {
2715                sb.append(Integer.toHexString(checksum[i]));
2716            }
2717            return sb.toString();
2718        }
2719
2720        private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
2721                throws IOException {
2722            byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
2723                    UserHandle.USER_OWNER);
2724            // has the widget state changed since last time?
2725            final File widgetFile = new File(mStateDir, pkgName + "_widget");
2726            final boolean priorStateExists = widgetFile.exists();
2727
2728            if (MORE_DEBUG) {
2729                if (priorStateExists || widgetState != null) {
2730                    Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
2731                            + " prior=" + priorStateExists);
2732                }
2733            }
2734
2735            if (!priorStateExists && widgetState == null) {
2736                // no prior state, no new state => nothing to do
2737                return;
2738            }
2739
2740            // if the new state is not null, we might need to compare checksums to
2741            // determine whether to update the widget blob in the archive.  If the
2742            // widget state *is* null, we know a priori at this point that we simply
2743            // need to commit a deletion for it.
2744            String newChecksum = null;
2745            if (widgetState != null) {
2746                newChecksum = SHA1Checksum(widgetState);
2747                if (priorStateExists) {
2748                    final String priorChecksum;
2749                    try (
2750                        FileInputStream fin = new FileInputStream(widgetFile);
2751                        DataInputStream in = new DataInputStream(fin)
2752                    ) {
2753                        priorChecksum = in.readUTF();
2754                    }
2755                    if (Objects.equals(newChecksum, priorChecksum)) {
2756                        // Same checksum => no state change => don't rewrite the widget data
2757                        return;
2758                    }
2759                }
2760            } // else widget state *became* empty, so we need to commit a deletion
2761
2762            BackupDataOutput out = new BackupDataOutput(fd);
2763            if (widgetState != null) {
2764                try (
2765                    FileOutputStream fout = new FileOutputStream(widgetFile);
2766                    DataOutputStream stateOut = new DataOutputStream(fout)
2767                ) {
2768                    stateOut.writeUTF(newChecksum);
2769                }
2770
2771                out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
2772                out.writeEntityData(widgetState, widgetState.length);
2773            } else {
2774                // Widget state for this app has been removed; commit a deletion
2775                out.writeEntityHeader(KEY_WIDGET_STATE, -1);
2776                widgetFile.delete();
2777            }
2778        }
2779
2780        @Override
2781        public void operationComplete() {
2782            // Okay, the agent successfully reported back to us!
2783            final String pkgName = mCurrentPackage.packageName;
2784            final long filepos = mBackupDataName.length();
2785            FileDescriptor fd = mBackupData.getFileDescriptor();
2786            try {
2787                // If it's a 3rd party app, see whether they wrote any protected keys
2788                // and complain mightily if they are attempting shenanigans.
2789                if (mCurrentPackage.applicationInfo != null &&
2790                        (mCurrentPackage.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
2791                    ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
2792                            ParcelFileDescriptor.MODE_READ_ONLY);
2793                    BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
2794                    try {
2795                        while (in.readNextHeader()) {
2796                            final String key = in.getKey();
2797                            if (key != null && key.charAt(0) >= 0xff00) {
2798                                // Not okay: crash them and bail.
2799                                failAgent(mAgentBinder, "Illegal backup key: " + key);
2800                                addBackupTrace("illegal key " + key + " from " + pkgName);
2801                                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
2802                                        "bad key");
2803                                mBackupHandler.removeMessages(MSG_TIMEOUT);
2804                                agentErrorCleanup();
2805                                // agentErrorCleanup() implicitly executes next state properly
2806                                return;
2807                            }
2808                            in.skipEntityData();
2809                        }
2810                    } finally {
2811                        if (readFd != null) {
2812                            readFd.close();
2813                        }
2814                    }
2815                }
2816
2817                // Piggyback the widget state payload, if any
2818                writeWidgetPayloadIfAppropriate(fd, pkgName);
2819            } catch (IOException e) {
2820                // Hard disk error; recovery/failure policy TBD.  For now roll back,
2821                // but we may want to consider this a transport-level failure (i.e.
2822                // we're in such a bad state that we can't contemplate doing backup
2823                // operations any more during this pass).
2824                Slog.w(TAG, "Unable to save widget state for " + pkgName);
2825                try {
2826                    Os.ftruncate(fd, filepos);
2827                } catch (ErrnoException ee) {
2828                    Slog.w(TAG, "Unable to roll back!");
2829                }
2830            }
2831
2832            // Spin the data off to the transport and proceed with the next stage.
2833            if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
2834                    + pkgName);
2835            mBackupHandler.removeMessages(MSG_TIMEOUT);
2836            clearAgentState();
2837            addBackupTrace("operation complete");
2838
2839            ParcelFileDescriptor backupData = null;
2840            mStatus = BackupTransport.TRANSPORT_OK;
2841            try {
2842                int size = (int) mBackupDataName.length();
2843                if (size > 0) {
2844                    if (mStatus == BackupTransport.TRANSPORT_OK) {
2845                        backupData = ParcelFileDescriptor.open(mBackupDataName,
2846                                ParcelFileDescriptor.MODE_READ_ONLY);
2847                        addBackupTrace("sending data to transport");
2848                        mStatus = mTransport.performBackup(mCurrentPackage, backupData);
2849                    }
2850
2851                    // TODO - We call finishBackup() for each application backed up, because
2852                    // we need to know now whether it succeeded or failed.  Instead, we should
2853                    // hold off on finishBackup() until the end, which implies holding off on
2854                    // renaming *all* the output state files (see below) until that happens.
2855
2856                    addBackupTrace("data delivered: " + mStatus);
2857                    if (mStatus == BackupTransport.TRANSPORT_OK) {
2858                        addBackupTrace("finishing op on transport");
2859                        mStatus = mTransport.finishBackup();
2860                        addBackupTrace("finished: " + mStatus);
2861                    } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
2862                        addBackupTrace("transport rejected package");
2863                    }
2864                } else {
2865                    if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
2866                    addBackupTrace("no data to send");
2867                }
2868
2869                if (mStatus == BackupTransport.TRANSPORT_OK) {
2870                    // After successful transport, delete the now-stale data
2871                    // and juggle the files so that next time we supply the agent
2872                    // with the new state file it just created.
2873                    mBackupDataName.delete();
2874                    mNewStateName.renameTo(mSavedStateName);
2875                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
2876                    logBackupComplete(pkgName);
2877                } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
2878                    // The transport has rejected backup of this specific package.  Roll it
2879                    // back but proceed with running the rest of the queue.
2880                    mBackupDataName.delete();
2881                    mNewStateName.delete();
2882                    EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
2883                } else {
2884                    // Actual transport-level failure to communicate the data to the backend
2885                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
2886                }
2887            } catch (Exception e) {
2888                Slog.e(TAG, "Transport error backing up " + pkgName, e);
2889                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
2890                mStatus = BackupTransport.TRANSPORT_ERROR;
2891            } finally {
2892                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
2893            }
2894
2895            final BackupState nextState;
2896            if (mStatus == BackupTransport.TRANSPORT_OK
2897                    || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
2898                // Success or single-package rejection.  Proceed with the next app if any,
2899                // otherwise we're done.
2900                nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
2901            } else {
2902                // Any other error here indicates a transport-level failure.  That means
2903                // we need to halt everything and reschedule everything for next time.
2904                revertAndEndBackup();
2905                nextState = BackupState.FINAL;
2906            }
2907
2908            executeNextState(nextState);
2909        }
2910
2911        @Override
2912        public void handleTimeout() {
2913            // Whoops, the current agent timed out running doBackup().  Tidy up and restage
2914            // it for the next time we run a backup pass.
2915            // !!! TODO: keep track of failure counts per agent, and blacklist those which
2916            // fail repeatedly (i.e. have proved themselves to be buggy).
2917            Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
2918            EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
2919                    "timeout");
2920            addBackupTrace("timeout of " + mCurrentPackage.packageName);
2921            agentErrorCleanup();
2922            dataChangedImpl(mCurrentPackage.packageName);
2923        }
2924
2925        void revertAndEndBackup() {
2926            if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
2927            addBackupTrace("transport error; reverting");
2928            for (BackupRequest request : mOriginalQueue) {
2929                dataChangedImpl(request.packageName);
2930            }
2931            // We also want to reset the backup schedule based on whatever
2932            // the transport suggests by way of retry/backoff time.
2933            restartBackupAlarm();
2934        }
2935
2936        void agentErrorCleanup() {
2937            mBackupDataName.delete();
2938            mNewStateName.delete();
2939            clearAgentState();
2940
2941            executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
2942        }
2943
2944        // Cleanup common to both success and failure cases
2945        void clearAgentState() {
2946            try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {}
2947            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
2948            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
2949            synchronized (mCurrentOpLock) {
2950                // Current-operation callback handling requires the validity of these various
2951                // bits of internal state as an invariant of the operation still being live.
2952                // This means we make sure to clear all of the state in unison inside the lock.
2953                mCurrentOperations.clear();
2954                mSavedState = mBackupData = mNewState = null;
2955            }
2956
2957            // If this was a pseudopackage there's no associated Activity Manager state
2958            if (mCurrentPackage.applicationInfo != null) {
2959                addBackupTrace("unbinding " + mCurrentPackage.packageName);
2960                try {  // unbind even on timeout, just in case
2961                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
2962                } catch (RemoteException e) { /* can't happen; activity manager is local */ }
2963            }
2964        }
2965
2966        void restartBackupAlarm() {
2967            addBackupTrace("setting backup trigger");
2968            synchronized (mQueueLock) {
2969                try {
2970                    startBackupAlarmsLocked(mTransport.requestBackupTime());
2971                } catch (RemoteException e) { /* cannot happen */ }
2972            }
2973        }
2974
2975        void executeNextState(BackupState nextState) {
2976            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
2977                    + this + " nextState=" + nextState);
2978            addBackupTrace("executeNextState => " + nextState);
2979            mCurrentState = nextState;
2980            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
2981            mBackupHandler.sendMessage(msg);
2982        }
2983    }
2984
2985
2986    // ----- Full backup/restore to a file/socket -----
2987
2988    class FullBackupObbConnection implements ServiceConnection {
2989        volatile IObbBackupService mService;
2990
2991        FullBackupObbConnection() {
2992            mService = null;
2993        }
2994
2995        public void establish() {
2996            if (DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this);
2997            Intent obbIntent = new Intent().setComponent(new ComponentName(
2998                    "com.android.sharedstoragebackup",
2999                    "com.android.sharedstoragebackup.ObbBackupService"));
3000            BackupManagerService.this.mContext.bindService(
3001                    obbIntent, this, Context.BIND_AUTO_CREATE);
3002        }
3003
3004        public void tearDown() {
3005            BackupManagerService.this.mContext.unbindService(this);
3006        }
3007
3008        public boolean backupObbs(PackageInfo pkg, OutputStream out) {
3009            boolean success = false;
3010            waitForConnection();
3011
3012            ParcelFileDescriptor[] pipes = null;
3013            try {
3014                pipes = ParcelFileDescriptor.createPipe();
3015                int token = generateToken();
3016                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
3017                mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
3018                routeSocketDataToOutput(pipes[0], out);
3019                success = waitUntilOperationComplete(token);
3020            } catch (Exception e) {
3021                Slog.w(TAG, "Unable to back up OBBs for " + pkg, e);
3022            } finally {
3023                try {
3024                    out.flush();
3025                    if (pipes != null) {
3026                        if (pipes[0] != null) pipes[0].close();
3027                        if (pipes[1] != null) pipes[1].close();
3028                    }
3029                } catch (IOException e) {
3030                    Slog.w(TAG, "I/O error closing down OBB backup", e);
3031                }
3032            }
3033            return success;
3034        }
3035
3036        public void restoreObbFile(String pkgName, ParcelFileDescriptor data,
3037                long fileSize, int type, String path, long mode, long mtime,
3038                int token, IBackupManager callbackBinder) {
3039            waitForConnection();
3040
3041            try {
3042                mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime,
3043                        token, callbackBinder);
3044            } catch (Exception e) {
3045                Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e);
3046            }
3047        }
3048
3049        private void waitForConnection() {
3050            synchronized (this) {
3051                while (mService == null) {
3052                    if (DEBUG) Slog.i(TAG, "...waiting for OBB service binding...");
3053                    try {
3054                        this.wait();
3055                    } catch (InterruptedException e) { /* never interrupted */ }
3056                }
3057                if (DEBUG) Slog.i(TAG, "Connected to OBB service; continuing");
3058            }
3059        }
3060
3061        @Override
3062        public void onServiceConnected(ComponentName name, IBinder service) {
3063            synchronized (this) {
3064                mService = IObbBackupService.Stub.asInterface(service);
3065                if (DEBUG) Slog.i(TAG, "OBB service connection " + mService
3066                        + " connected on " + this);
3067                this.notifyAll();
3068            }
3069        }
3070
3071        @Override
3072        public void onServiceDisconnected(ComponentName name) {
3073            synchronized (this) {
3074                mService = null;
3075                if (DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this);
3076                this.notifyAll();
3077            }
3078        }
3079
3080    }
3081
3082    private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
3083            throws IOException {
3084        FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
3085        DataInputStream in = new DataInputStream(raw);
3086
3087        byte[] buffer = new byte[32 * 1024];
3088        int chunkTotal;
3089        while ((chunkTotal = in.readInt()) > 0) {
3090            while (chunkTotal > 0) {
3091                int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
3092                int nRead = in.read(buffer, 0, toRead);
3093                out.write(buffer, 0, nRead);
3094                chunkTotal -= nRead;
3095            }
3096        }
3097    }
3098
3099    // Core logic for performing one package's full backup, gathering the tarball from the
3100    // application and emitting it to the designated OutputStream.
3101    class FullBackupEngine {
3102        OutputStream mOutput;
3103        IFullBackupRestoreObserver mObserver;
3104        File mFilesDir;
3105        File mManifestFile;
3106        File mMetadataFile;
3107        boolean mIncludeApks;
3108
3109        class FullBackupRunner implements Runnable {
3110            PackageInfo mPackage;
3111            byte[] mWidgetData;
3112            IBackupAgent mAgent;
3113            ParcelFileDescriptor mPipe;
3114            int mToken;
3115            boolean mSendApk;
3116            boolean mWriteManifest;
3117
3118            FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
3119                    int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
3120                            throws IOException {
3121                mPackage = pack;
3122                mWidgetData = widgetData;
3123                mAgent = agent;
3124                mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
3125                mToken = token;
3126                mSendApk = sendApk;
3127                mWriteManifest = writeManifest;
3128            }
3129
3130            @Override
3131            public void run() {
3132                try {
3133                    BackupDataOutput output = new BackupDataOutput(
3134                            mPipe.getFileDescriptor());
3135
3136                    if (mWriteManifest) {
3137                        final boolean writeWidgetData = mWidgetData != null;
3138                        if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
3139                        writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
3140                        FullBackup.backupToTar(mPackage.packageName, null, null,
3141                                mFilesDir.getAbsolutePath(),
3142                                mManifestFile.getAbsolutePath(),
3143                                output);
3144                        mManifestFile.delete();
3145
3146                        // We only need to write a metadata file if we have widget data to stash
3147                        if (writeWidgetData) {
3148                            writeMetadata(mPackage, mMetadataFile, mWidgetData);
3149                            FullBackup.backupToTar(mPackage.packageName, null, null,
3150                                    mFilesDir.getAbsolutePath(),
3151                                    mMetadataFile.getAbsolutePath(),
3152                                    output);
3153                            mMetadataFile.delete();
3154                        }
3155                    }
3156
3157                    if (mSendApk) {
3158                        writeApkToBackup(mPackage, output);
3159                    }
3160
3161                    if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
3162                    prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null);
3163                    mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
3164                } catch (IOException e) {
3165                    Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
3166                } catch (RemoteException e) {
3167                    Slog.e(TAG, "Remote agent vanished during full backup of "
3168                            + mPackage.packageName);
3169                } finally {
3170                    try {
3171                        mPipe.close();
3172                    } catch (IOException e) {}
3173                }
3174            }
3175        }
3176
3177        FullBackupEngine(OutputStream output, String packageName, boolean alsoApks) {
3178            mOutput = output;
3179            mIncludeApks = alsoApks;
3180            mFilesDir = new File("/data/system");
3181            mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
3182            mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
3183        }
3184
3185
3186        public int backupOnePackage(PackageInfo pkg) throws RemoteException {
3187            int result = BackupTransport.TRANSPORT_OK;
3188            Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
3189
3190            IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
3191                    IApplicationThread.BACKUP_MODE_FULL);
3192            if (agent != null) {
3193                ParcelFileDescriptor[] pipes = null;
3194                try {
3195                    pipes = ParcelFileDescriptor.createPipe();
3196
3197                    ApplicationInfo app = pkg.applicationInfo;
3198                    final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
3199                    final boolean sendApk = mIncludeApks
3200                            && !isSharedStorage
3201                            && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
3202                            && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
3203                                (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
3204
3205                    byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
3206                            UserHandle.USER_OWNER);
3207
3208                    final int token = generateToken();
3209                    FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
3210                            token, sendApk, !isSharedStorage, widgetBlob);
3211                    pipes[1].close();   // the runner has dup'd it
3212                    pipes[1] = null;
3213                    Thread t = new Thread(runner, "app-data-runner");
3214                    t.start();
3215
3216                    // Now pull data from the app and stuff it into the output
3217                    try {
3218                        routeSocketDataToOutput(pipes[0], mOutput);
3219                    } catch (IOException e) {
3220                        Slog.i(TAG, "Caught exception reading from agent", e);
3221                        result = BackupTransport.AGENT_ERROR;
3222                    }
3223
3224                    if (!waitUntilOperationComplete(token)) {
3225                        Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
3226                        result = BackupTransport.AGENT_ERROR;
3227                    } else {
3228                        if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
3229                    }
3230
3231                } catch (IOException e) {
3232                    Slog.e(TAG, "Error backing up " + pkg.packageName, e);
3233                    result = BackupTransport.AGENT_ERROR;
3234                } finally {
3235                    try {
3236                        // flush after every package
3237                        mOutput.flush();
3238                        if (pipes != null) {
3239                            if (pipes[0] != null) pipes[0].close();
3240                            if (pipes[1] != null) pipes[1].close();
3241                        }
3242                    } catch (IOException e) {
3243                        Slog.w(TAG, "Error bringing down backup stack");
3244                        result = BackupTransport.TRANSPORT_ERROR;
3245                    }
3246                }
3247            } else {
3248                Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
3249                result = BackupTransport.AGENT_ERROR;
3250            }
3251            tearDown(pkg);
3252            return result;
3253        }
3254
3255        private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
3256            // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
3257            // TODO: handle backing up split APKs
3258            final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
3259            final String apkDir = new File(appSourceDir).getParent();
3260            FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
3261                    apkDir, appSourceDir, output);
3262
3263            // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM
3264            // doesn't have access to external storage.
3265
3266            // Save associated .obb content if it exists and we did save the apk
3267            // check for .obb and save those too
3268            final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_OWNER);
3269            final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0];
3270            if (obbDir != null) {
3271                if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
3272                File[] obbFiles = obbDir.listFiles();
3273                if (obbFiles != null) {
3274                    final String obbDirName = obbDir.getAbsolutePath();
3275                    for (File obb : obbFiles) {
3276                        FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
3277                                obbDirName, obb.getAbsolutePath(), output);
3278                    }
3279                }
3280            }
3281        }
3282
3283        private void writeAppManifest(PackageInfo pkg, File manifestFile,
3284                boolean withApk, boolean withWidgets) throws IOException {
3285            // Manifest format. All data are strings ending in LF:
3286            //     BACKUP_MANIFEST_VERSION, currently 1
3287            //
3288            // Version 1:
3289            //     package name
3290            //     package's versionCode
3291            //     platform versionCode
3292            //     getInstallerPackageName() for this package (maybe empty)
3293            //     boolean: "1" if archive includes .apk; any other string means not
3294            //     number of signatures == N
3295            // N*:    signature byte array in ascii format per Signature.toCharsString()
3296            StringBuilder builder = new StringBuilder(4096);
3297            StringBuilderPrinter printer = new StringBuilderPrinter(builder);
3298
3299            printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
3300            printer.println(pkg.packageName);
3301            printer.println(Integer.toString(pkg.versionCode));
3302            printer.println(Integer.toString(Build.VERSION.SDK_INT));
3303
3304            String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
3305            printer.println((installerName != null) ? installerName : "");
3306
3307            printer.println(withApk ? "1" : "0");
3308            if (pkg.signatures == null) {
3309                printer.println("0");
3310            } else {
3311                printer.println(Integer.toString(pkg.signatures.length));
3312                for (Signature sig : pkg.signatures) {
3313                    printer.println(sig.toCharsString());
3314                }
3315            }
3316
3317            FileOutputStream outstream = new FileOutputStream(manifestFile);
3318            outstream.write(builder.toString().getBytes());
3319            outstream.close();
3320
3321            // We want the manifest block in the archive stream to be idempotent:
3322            // each time we generate a backup stream for the app, we want the manifest
3323            // block to be identical.  The underlying tar mechanism sees it as a file,
3324            // though, and will propagate its mtime, causing the tar header to vary.
3325            // Avoid this problem by pinning the mtime to zero.
3326            manifestFile.setLastModified(0);
3327        }
3328
3329        // Widget metadata format. All header entries are strings ending in LF:
3330        //
3331        // Version 1 header:
3332        //     BACKUP_METADATA_VERSION, currently "1"
3333        //     package name
3334        //
3335        // File data (all integers are binary in network byte order)
3336        // *N: 4 : integer token identifying which metadata blob
3337        //     4 : integer size of this blob = N
3338        //     N : raw bytes of this metadata blob
3339        //
3340        // Currently understood blobs (always in network byte order):
3341        //
3342        //     widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
3343        //
3344        // Unrecognized blobs are *ignored*, not errors.
3345        private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
3346                throws IOException {
3347            StringBuilder b = new StringBuilder(512);
3348            StringBuilderPrinter printer = new StringBuilderPrinter(b);
3349            printer.println(Integer.toString(BACKUP_METADATA_VERSION));
3350            printer.println(pkg.packageName);
3351
3352            FileOutputStream fout = new FileOutputStream(destination);
3353            BufferedOutputStream bout = new BufferedOutputStream(fout);
3354            DataOutputStream out = new DataOutputStream(bout);
3355            bout.write(b.toString().getBytes());    // bypassing DataOutputStream
3356
3357            if (widgetData != null && widgetData.length > 0) {
3358                out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
3359                out.writeInt(widgetData.length);
3360                out.write(widgetData);
3361            }
3362            bout.flush();
3363            out.close();
3364
3365            // As with the manifest file, guarantee idempotence of the archive metadata
3366            // for the widget block by using a fixed mtime on the transient file.
3367            destination.setLastModified(0);
3368        }
3369
3370        private void tearDown(PackageInfo pkg) {
3371            if (pkg != null) {
3372                final ApplicationInfo app = pkg.applicationInfo;
3373                if (app != null) {
3374                    try {
3375                        // unbind and tidy up even on timeout or failure, just in case
3376                        mActivityManager.unbindBackupAgent(app);
3377
3378                        // The agent was running with a stub Application object, so shut it down.
3379                        if (app.uid != Process.SYSTEM_UID
3380                                && app.uid != Process.PHONE_UID) {
3381                            if (MORE_DEBUG) Slog.d(TAG, "Backup complete, killing host process");
3382                            mActivityManager.killApplicationProcess(app.processName, app.uid);
3383                        } else {
3384                            if (MORE_DEBUG) Slog.d(TAG, "Not killing after backup: " + app.processName);
3385                        }
3386                    } catch (RemoteException e) {
3387                        Slog.d(TAG, "Lost app trying to shut down");
3388                    }
3389                }
3390            }
3391        }
3392    }
3393
3394    // Generic driver skeleton for full backup operations
3395    abstract class FullBackupTask implements Runnable {
3396        IFullBackupRestoreObserver mObserver;
3397
3398        FullBackupTask(IFullBackupRestoreObserver observer) {
3399            mObserver = observer;
3400        }
3401
3402        // wrappers for observer use
3403        final void sendStartBackup() {
3404            if (mObserver != null) {
3405                try {
3406                    mObserver.onStartBackup();
3407                } catch (RemoteException e) {
3408                    Slog.w(TAG, "full backup observer went away: startBackup");
3409                    mObserver = null;
3410                }
3411            }
3412        }
3413
3414        final void sendOnBackupPackage(String name) {
3415            if (mObserver != null) {
3416                try {
3417                    // TODO: use a more user-friendly name string
3418                    mObserver.onBackupPackage(name);
3419                } catch (RemoteException e) {
3420                    Slog.w(TAG, "full backup observer went away: backupPackage");
3421                    mObserver = null;
3422                }
3423            }
3424        }
3425
3426        final void sendEndBackup() {
3427            if (mObserver != null) {
3428                try {
3429                    mObserver.onEndBackup();
3430                } catch (RemoteException e) {
3431                    Slog.w(TAG, "full backup observer went away: endBackup");
3432                    mObserver = null;
3433                }
3434            }
3435        }
3436    }
3437
3438    boolean deviceIsEncrypted() {
3439        try {
3440            return mMountService.getEncryptionState()
3441                     != IMountService.ENCRYPTION_STATE_NONE
3442                && mMountService.getPasswordType()
3443                     != StorageManager.CRYPT_TYPE_DEFAULT;
3444        } catch (Exception e) {
3445            // If we can't talk to the mount service we have a serious problem; fail
3446            // "secure" i.e. assuming that the device is encrypted.
3447            Slog.e(TAG, "Unable to communicate with mount service: " + e.getMessage());
3448            return true;
3449        }
3450    }
3451
3452    // Full backup task variant used for adb backup
3453    class PerformAdbBackupTask extends FullBackupTask {
3454        FullBackupEngine mBackupEngine;
3455        final AtomicBoolean mLatch;
3456
3457        ParcelFileDescriptor mOutputFile;
3458        DeflaterOutputStream mDeflater;
3459        boolean mIncludeApks;
3460        boolean mIncludeObbs;
3461        boolean mIncludeShared;
3462        boolean mDoWidgets;
3463        boolean mAllApps;
3464        boolean mIncludeSystem;
3465        boolean mCompress;
3466        ArrayList<String> mPackages;
3467        String mCurrentPassword;
3468        String mEncryptPassword;
3469
3470        PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
3471                boolean includeApks, boolean includeObbs, boolean includeShared,
3472                boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
3473                boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch) {
3474            super(observer);
3475            mLatch = latch;
3476
3477            mOutputFile = fd;
3478            mIncludeApks = includeApks;
3479            mIncludeObbs = includeObbs;
3480            mIncludeShared = includeShared;
3481            mDoWidgets = doWidgets;
3482            mAllApps = doAllApps;
3483            mIncludeSystem = doSystem;
3484            mPackages = (packages == null)
3485                    ? new ArrayList<String>()
3486                    : new ArrayList<String>(Arrays.asList(packages));
3487            mCurrentPassword = curPassword;
3488            // when backing up, if there is a current backup password, we require that
3489            // the user use a nonempty encryption password as well.  if one is supplied
3490            // in the UI we use that, but if the UI was left empty we fall back to the
3491            // current backup password (which was supplied by the user as well).
3492            if (encryptPassword == null || "".equals(encryptPassword)) {
3493                mEncryptPassword = curPassword;
3494            } else {
3495                mEncryptPassword = encryptPassword;
3496            }
3497            mCompress = doCompress;
3498        }
3499
3500        void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
3501            for (String pkgName : pkgNames) {
3502                if (!set.containsKey(pkgName)) {
3503                    try {
3504                        PackageInfo info = mPackageManager.getPackageInfo(pkgName,
3505                                PackageManager.GET_SIGNATURES);
3506                        set.put(pkgName, info);
3507                    } catch (NameNotFoundException e) {
3508                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
3509                    }
3510                }
3511            }
3512        }
3513
3514        private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
3515                OutputStream ofstream) throws Exception {
3516            // User key will be used to encrypt the master key.
3517            byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE);
3518            SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt,
3519                    PBKDF2_HASH_ROUNDS);
3520
3521            // the master key is random for each backup
3522            byte[] masterPw = new byte[256 / 8];
3523            mRng.nextBytes(masterPw);
3524            byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE);
3525
3526            // primary encryption of the datastream with the random key
3527            Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
3528            SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES");
3529            c.init(Cipher.ENCRYPT_MODE, masterKeySpec);
3530            OutputStream finalOutput = new CipherOutputStream(ofstream, c);
3531
3532            // line 4: name of encryption algorithm
3533            headerbuf.append(ENCRYPTION_ALGORITHM_NAME);
3534            headerbuf.append('\n');
3535            // line 5: user password salt [hex]
3536            headerbuf.append(byteArrayToHex(newUserSalt));
3537            headerbuf.append('\n');
3538            // line 6: master key checksum salt [hex]
3539            headerbuf.append(byteArrayToHex(checksumSalt));
3540            headerbuf.append('\n');
3541            // line 7: number of PBKDF2 rounds used [decimal]
3542            headerbuf.append(PBKDF2_HASH_ROUNDS);
3543            headerbuf.append('\n');
3544
3545            // line 8: IV of the user key [hex]
3546            Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding");
3547            mkC.init(Cipher.ENCRYPT_MODE, userKey);
3548
3549            byte[] IV = mkC.getIV();
3550            headerbuf.append(byteArrayToHex(IV));
3551            headerbuf.append('\n');
3552
3553            // line 9: master IV + key blob, encrypted by the user key [hex].  Blob format:
3554            //    [byte] IV length = Niv
3555            //    [array of Niv bytes] IV itself
3556            //    [byte] master key length = Nmk
3557            //    [array of Nmk bytes] master key itself
3558            //    [byte] MK checksum hash length = Nck
3559            //    [array of Nck bytes] master key checksum hash
3560            //
3561            // The checksum is the (master key + checksum salt), run through the
3562            // stated number of PBKDF2 rounds
3563            IV = c.getIV();
3564            byte[] mk = masterKeySpec.getEncoded();
3565            byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(),
3566                    checksumSalt, PBKDF2_HASH_ROUNDS);
3567
3568            ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length
3569                    + checksum.length + 3);
3570            DataOutputStream mkOut = new DataOutputStream(blob);
3571            mkOut.writeByte(IV.length);
3572            mkOut.write(IV);
3573            mkOut.writeByte(mk.length);
3574            mkOut.write(mk);
3575            mkOut.writeByte(checksum.length);
3576            mkOut.write(checksum);
3577            mkOut.flush();
3578            byte[] encryptedMk = mkC.doFinal(blob.toByteArray());
3579            headerbuf.append(byteArrayToHex(encryptedMk));
3580            headerbuf.append('\n');
3581
3582            return finalOutput;
3583        }
3584
3585        private void finalizeBackup(OutputStream out) {
3586            try {
3587                // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes.
3588                byte[] eof = new byte[512 * 2]; // newly allocated == zero filled
3589                out.write(eof);
3590            } catch (IOException e) {
3591                Slog.w(TAG, "Error attempting to finalize backup stream");
3592            }
3593        }
3594
3595        @Override
3596        public void run() {
3597            Slog.i(TAG, "--- Performing full-dataset adb backup ---");
3598
3599            TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
3600            FullBackupObbConnection obbConnection = new FullBackupObbConnection();
3601            obbConnection.establish();  // we'll want this later
3602
3603            sendStartBackup();
3604
3605            // doAllApps supersedes the package set if any
3606            if (mAllApps) {
3607                List<PackageInfo> allPackages = mPackageManager.getInstalledPackages(
3608                        PackageManager.GET_SIGNATURES);
3609                for (int i = 0; i < allPackages.size(); i++) {
3610                    PackageInfo pkg = allPackages.get(i);
3611                    // Exclude system apps if we've been asked to do so
3612                    if (mIncludeSystem == true
3613                            || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
3614                        packagesToBackup.put(pkg.packageName, pkg);
3615                    }
3616                }
3617            }
3618
3619            // If we're doing widget state as well, ensure that we have all the involved
3620            // host & provider packages in the set
3621            if (mDoWidgets) {
3622                List<String> pkgs =
3623                        AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_OWNER);
3624                if (pkgs != null) {
3625                    if (MORE_DEBUG) {
3626                        Slog.i(TAG, "Adding widget participants to backup set:");
3627                        StringBuilder sb = new StringBuilder(128);
3628                        sb.append("   ");
3629                        for (String s : pkgs) {
3630                            sb.append(' ');
3631                            sb.append(s);
3632                        }
3633                        Slog.i(TAG, sb.toString());
3634                    }
3635                    addPackagesToSet(packagesToBackup, pkgs);
3636                }
3637            }
3638
3639            // Now process the command line argument packages, if any. Note that explicitly-
3640            // named system-partition packages will be included even if includeSystem was
3641            // set to false.
3642            if (mPackages != null) {
3643                addPackagesToSet(packagesToBackup, mPackages);
3644            }
3645
3646            // Now we cull any inapplicable / inappropriate packages from the set.  This
3647            // includes the special shared-storage agent package; we handle that one
3648            // explicitly at the end of the backup pass.
3649            Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
3650            while (iter.hasNext()) {
3651                PackageInfo pkg = iter.next().getValue();
3652                if (!appIsEligibleForBackup(pkg.applicationInfo)) {
3653                    iter.remove();
3654                }
3655            }
3656
3657            // flatten the set of packages now so we can explicitly control the ordering
3658            ArrayList<PackageInfo> backupQueue =
3659                    new ArrayList<PackageInfo>(packagesToBackup.values());
3660            FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
3661            OutputStream out = null;
3662
3663            PackageInfo pkg = null;
3664            try {
3665                boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
3666
3667                // Only allow encrypted backups of encrypted devices
3668                if (deviceIsEncrypted() && !encrypting) {
3669                    Slog.e(TAG, "Unencrypted backup of encrypted device; aborting");
3670                    return;
3671                }
3672
3673                OutputStream finalOutput = ofstream;
3674
3675                // Verify that the given password matches the currently-active
3676                // backup password, if any
3677                if (!backupPasswordMatches(mCurrentPassword)) {
3678                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
3679                    return;
3680                }
3681
3682                // Write the global file header.  All strings are UTF-8 encoded; lines end
3683                // with a '\n' byte.  Actual backup data begins immediately following the
3684                // final '\n'.
3685                //
3686                // line 1: "ANDROID BACKUP"
3687                // line 2: backup file format version, currently "2"
3688                // line 3: compressed?  "0" if not compressed, "1" if compressed.
3689                // line 4: name of encryption algorithm [currently only "none" or "AES-256"]
3690                //
3691                // When line 4 is not "none", then additional header data follows:
3692                //
3693                // line 5: user password salt [hex]
3694                // line 6: master key checksum salt [hex]
3695                // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal]
3696                // line 8: IV of the user key [hex]
3697                // line 9: master key blob [hex]
3698                //     IV of the master key, master key itself, master key checksum hash
3699                //
3700                // The master key checksum is the master key plus its checksum salt, run through
3701                // 10k rounds of PBKDF2.  This is used to verify that the user has supplied the
3702                // correct password for decrypting the archive:  the master key decrypted from
3703                // the archive using the user-supplied password is also run through PBKDF2 in
3704                // this way, and if the result does not match the checksum as stored in the
3705                // archive, then we know that the user-supplied password does not match the
3706                // archive's.
3707                StringBuilder headerbuf = new StringBuilder(1024);
3708
3709                headerbuf.append(BACKUP_FILE_HEADER_MAGIC);
3710                headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n
3711                headerbuf.append(mCompress ? "\n1\n" : "\n0\n");
3712
3713                try {
3714                    // Set up the encryption stage if appropriate, and emit the correct header
3715                    if (encrypting) {
3716                        finalOutput = emitAesBackupHeader(headerbuf, finalOutput);
3717                    } else {
3718                        headerbuf.append("none\n");
3719                    }
3720
3721                    byte[] header = headerbuf.toString().getBytes("UTF-8");
3722                    ofstream.write(header);
3723
3724                    // Set up the compression stage feeding into the encryption stage (if any)
3725                    if (mCompress) {
3726                        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
3727                        finalOutput = new DeflaterOutputStream(finalOutput, deflater, true);
3728                    }
3729
3730                    out = finalOutput;
3731                } catch (Exception e) {
3732                    // Should never happen!
3733                    Slog.e(TAG, "Unable to emit archive header", e);
3734                    return;
3735                }
3736
3737                // Shared storage if requested
3738                if (mIncludeShared) {
3739                    try {
3740                        pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
3741                        backupQueue.add(pkg);
3742                    } catch (NameNotFoundException e) {
3743                        Slog.e(TAG, "Unable to find shared-storage backup handler");
3744                    }
3745                }
3746
3747                // Now actually run the constructed backup sequence
3748                int N = backupQueue.size();
3749                for (int i = 0; i < N; i++) {
3750                    pkg = backupQueue.get(i);
3751                    final boolean isSharedStorage =
3752                            pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
3753
3754                    mBackupEngine = new FullBackupEngine(out, pkg.packageName, mIncludeApks);
3755                    sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
3756                    mBackupEngine.backupOnePackage(pkg);
3757
3758                    // after the app's agent runs to handle its private filesystem
3759                    // contents, back up any OBB content it has on its behalf.
3760                    if (mIncludeObbs) {
3761                        boolean obbOkay = obbConnection.backupObbs(pkg, out);
3762                        if (!obbOkay) {
3763                            throw new RuntimeException("Failure writing OBB stack for " + pkg);
3764                        }
3765                    }
3766                }
3767
3768                // Done!
3769                finalizeBackup(out);
3770            } catch (RemoteException e) {
3771                Slog.e(TAG, "App died during full backup");
3772            } catch (Exception e) {
3773                Slog.e(TAG, "Internal exception during full backup", e);
3774            } finally {
3775                try {
3776                    if (out != null) out.close();
3777                    mOutputFile.close();
3778                } catch (IOException e) {
3779                    /* nothing we can do about this */
3780                }
3781                synchronized (mCurrentOpLock) {
3782                    mCurrentOperations.clear();
3783                }
3784                synchronized (mLatch) {
3785                    mLatch.set(true);
3786                    mLatch.notifyAll();
3787                }
3788                sendEndBackup();
3789                obbConnection.tearDown();
3790                if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
3791                mWakelock.release();
3792            }
3793        }
3794    }
3795
3796    // Full backup task extension used for transport-oriented operation
3797    class PerformFullTransportBackupTask extends FullBackupTask {
3798        static final String TAG = "PFTBT";
3799        ArrayList<PackageInfo> mPackages;
3800        boolean mUpdateSchedule;
3801        AtomicBoolean mLatch;
3802        AtomicBoolean mKeepRunning;     // signal from job scheduler
3803        FullBackupJob mJob;             // if a scheduled job needs to be finished afterwards
3804
3805        PerformFullTransportBackupTask(IFullBackupRestoreObserver observer,
3806                String[] whichPackages, boolean updateSchedule,
3807                FullBackupJob runningJob, AtomicBoolean latch) {
3808            super(observer);
3809            mUpdateSchedule = updateSchedule;
3810            mLatch = latch;
3811            mKeepRunning = new AtomicBoolean(true);
3812            mJob = runningJob;
3813            mPackages = new ArrayList<PackageInfo>(whichPackages.length);
3814
3815            for (String pkg : whichPackages) {
3816                try {
3817                    PackageInfo info = mPackageManager.getPackageInfo(pkg,
3818                            PackageManager.GET_SIGNATURES);
3819                    if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0
3820                            || pkg.equals(SHARED_BACKUP_AGENT_PACKAGE)) {
3821                        // Cull any packages that have indicated that backups are not permitted,
3822                        // as well as any explicit mention of the 'special' shared-storage agent
3823                        // package (we handle that one at the end).
3824                        if (MORE_DEBUG) {
3825                            Slog.d(TAG, "Ignoring opted-out package " + pkg);
3826                        }
3827                        continue;
3828                    } else if ((info.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
3829                            && (info.applicationInfo.backupAgentName == null)) {
3830                        // Cull any packages that run as system-domain uids but do not define their
3831                        // own backup agents
3832                        if (MORE_DEBUG) {
3833                            Slog.d(TAG, "Ignoring non-agent system package " + pkg);
3834                        }
3835                        continue;
3836                    }
3837                    mPackages.add(info);
3838                } catch (NameNotFoundException e) {
3839                    Slog.i(TAG, "Requested package " + pkg + " not found; ignoring");
3840                }
3841            }
3842        }
3843
3844        public void setRunning(boolean running) {
3845            mKeepRunning.set(running);
3846        }
3847
3848        @Override
3849        public void run() {
3850            // data from the app, passed to us for bridging to the transport
3851            ParcelFileDescriptor[] enginePipes = null;
3852
3853            // Pipe through which we write data to the transport
3854            ParcelFileDescriptor[] transportPipes = null;
3855
3856            PackageInfo currentPackage;
3857
3858            try {
3859                if (!mEnabled || !mProvisioned) {
3860                    // Backups are globally disabled, so don't proceed.
3861                    if (DEBUG) {
3862                        Slog.i(TAG, "full backup requested but e=" + mEnabled
3863                                + " p=" + mProvisioned + "; ignoring");
3864                    }
3865                    mUpdateSchedule = false;
3866                    return;
3867                }
3868
3869                IBackupTransport transport = getTransport(mCurrentTransport);
3870                if (transport == null) {
3871                    Slog.w(TAG, "Transport not present; full data backup not performed");
3872                    return;
3873                }
3874
3875                // Set up to send data to the transport
3876                final int N = mPackages.size();
3877                for (int i = 0; i < N; i++) {
3878                    currentPackage = mPackages.get(i);
3879                    if (DEBUG) {
3880                        Slog.i(TAG, "Initiating full-data transport backup of "
3881                                + currentPackage.packageName);
3882                    }
3883                    EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE,
3884                            currentPackage.packageName);
3885
3886                    transportPipes = ParcelFileDescriptor.createPipe();
3887
3888                    // Tell the transport the data's coming
3889                    int result = transport.performFullBackup(currentPackage,
3890                            transportPipes[0]);
3891                    if (result == BackupTransport.TRANSPORT_OK) {
3892                        // The transport has its own copy of the read end of the pipe,
3893                        // so close ours now
3894                        transportPipes[0].close();
3895                        transportPipes[0] = null;
3896
3897                        // Now set up the backup engine / data source end of things
3898                        enginePipes = ParcelFileDescriptor.createPipe();
3899                        AtomicBoolean runnerLatch = new AtomicBoolean(false);
3900                        SinglePackageBackupRunner backupRunner =
3901                                new SinglePackageBackupRunner(enginePipes[1], currentPackage,
3902                                        runnerLatch);
3903                        // The runner dup'd the pipe half, so we close it here
3904                        enginePipes[1].close();
3905                        enginePipes[1] = null;
3906
3907                        // Spin off the runner to fetch the app's data and pipe it
3908                        // into the engine pipes
3909                        (new Thread(backupRunner, "package-backup-bridge")).start();
3910
3911                        // Read data off the engine pipe and pass it to the transport
3912                        // pipe until we hit EOD on the input stream.
3913                        FileInputStream in = new FileInputStream(
3914                                enginePipes[0].getFileDescriptor());
3915                        FileOutputStream out = new FileOutputStream(
3916                                transportPipes[1].getFileDescriptor());
3917                        byte[] buffer = new byte[8192];
3918                        int nRead = 0;
3919                        do {
3920                            if (!mKeepRunning.get()) {
3921                                if (DEBUG_SCHEDULING) {
3922                                    Slog.i(TAG, "Full backup task told to stop");
3923                                }
3924                                break;
3925                            }
3926                            nRead = in.read(buffer);
3927                            if (nRead > 0) {
3928                                out.write(buffer, 0, nRead);
3929                                result = transport.sendBackupData(nRead);
3930                            }
3931                        } while (nRead > 0 && result == BackupTransport.TRANSPORT_OK);
3932
3933                        // If we've lost our running criteria, tell the transport to cancel
3934                        // and roll back this (partial) backup payload; otherwise tell it
3935                        // that we've reached the clean finish state.
3936                        if (!mKeepRunning.get()) {
3937                            result = BackupTransport.TRANSPORT_ERROR;
3938                            transport.cancelFullBackup();
3939                        } else {
3940                            // If we were otherwise in a good state, now interpret the final
3941                            // result based on what finishBackup() returns.  If we're in a
3942                            // failure case already, preserve that result and ignore whatever
3943                            // finishBackup() reports.
3944                            final int finishResult = transport.finishBackup();
3945                            if (result == BackupTransport.TRANSPORT_OK) {
3946                                result = finishResult;
3947                            }
3948                        }
3949
3950                        if (MORE_DEBUG) {
3951                            Slog.i(TAG, "Done trying to send backup data: result=" + result);
3952                        }
3953
3954                        if (result != BackupTransport.TRANSPORT_OK) {
3955                            Slog.e(TAG, "Error " + result
3956                                    + " backing up " + currentPackage.packageName);
3957                        }
3958                    }
3959
3960                    // Roll this package to the end of the backup queue if we're
3961                    // in a queue-driven mode (regardless of success/failure)
3962                    if (mUpdateSchedule) {
3963                        enqueueFullBackup(currentPackage.packageName,
3964                                System.currentTimeMillis());
3965                    }
3966
3967                    if (result == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3968                        if (DEBUG) {
3969                            Slog.i(TAG, "Transport rejected backup of "
3970                                    + currentPackage.packageName
3971                                    + ", skipping");
3972                        }
3973                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE,
3974                                currentPackage.packageName, "transport rejected");
3975                        // do nothing, clean up, and continue looping
3976                    } else if (result != BackupTransport.TRANSPORT_OK) {
3977                        if (DEBUG) {
3978                            Slog.i(TAG, "Transport failed; aborting backup: " + result);
3979                            EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
3980                            return;
3981                        }
3982                    } else {
3983                        // Success!
3984                        EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS,
3985                                currentPackage.packageName);
3986                        logBackupComplete(currentPackage.packageName);
3987                    }
3988                    cleanUpPipes(transportPipes);
3989                    cleanUpPipes(enginePipes);
3990                    currentPackage = null;
3991                }
3992
3993                if (DEBUG) {
3994                    Slog.i(TAG, "Full backup completed.");
3995                }
3996            } catch (Exception e) {
3997                Slog.w(TAG, "Exception trying full transport backup", e);
3998            } finally {
3999                cleanUpPipes(transportPipes);
4000                cleanUpPipes(enginePipes);
4001
4002                if (mJob != null) {
4003                    mJob.finishBackupPass();
4004                }
4005
4006                synchronized (mQueueLock) {
4007                    mRunningFullBackupTask = null;
4008                }
4009
4010                synchronized (mLatch) {
4011                    mLatch.set(true);
4012                    mLatch.notifyAll();
4013                }
4014
4015                // Now that we're actually done with schedule-driven work, reschedule
4016                // the next pass based on the new queue state.
4017                if (mUpdateSchedule) {
4018                    scheduleNextFullBackupJob();
4019                }
4020            }
4021        }
4022
4023        void cleanUpPipes(ParcelFileDescriptor[] pipes) {
4024            if (pipes != null) {
4025                if (pipes[0] != null) {
4026                    ParcelFileDescriptor fd = pipes[0];
4027                    pipes[0] = null;
4028                    try {
4029                        fd.close();
4030                    } catch (IOException e) {
4031                        Slog.w(TAG, "Unable to close pipe!");
4032                    }
4033                }
4034                if (pipes[1] != null) {
4035                    ParcelFileDescriptor fd = pipes[1];
4036                    pipes[1] = null;
4037                    try {
4038                        fd.close();
4039                    } catch (IOException e) {
4040                        Slog.w(TAG, "Unable to close pipe!");
4041                    }
4042                }
4043            }
4044        }
4045
4046        // Run the backup and pipe it back to the given socket -- expects to run on
4047        // a standalone thread.  The  runner owns this half of the pipe, and closes
4048        // it to indicate EOD to the other end.
4049        class SinglePackageBackupRunner implements Runnable {
4050            final ParcelFileDescriptor mOutput;
4051            final PackageInfo mTarget;
4052            final AtomicBoolean mLatch;
4053
4054            SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
4055                    AtomicBoolean latch) throws IOException {
4056                int oldfd = output.getFd();
4057                mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
4058                mTarget = target;
4059                mLatch = latch;
4060            }
4061
4062            @Override
4063            public void run() {
4064                try {
4065                    FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
4066                    FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName, false);
4067                    engine.backupOnePackage(mTarget);
4068                } catch (Exception e) {
4069                    Slog.e(TAG, "Exception during full package backup of " + mTarget);
4070                } finally {
4071                    synchronized (mLatch) {
4072                        mLatch.set(true);
4073                        mLatch.notifyAll();
4074                    }
4075                    try {
4076                        mOutput.close();
4077                    } catch (IOException e) {
4078                        Slog.w(TAG, "Error closing transport pipe in runner");
4079                    }
4080                }
4081            }
4082
4083        }
4084    }
4085
4086    // ----- Full-data backup scheduling -----
4087
4088    /**
4089     * Schedule a job to tell us when it's a good time to run a full backup
4090     */
4091    void scheduleNextFullBackupJob() {
4092        synchronized (mQueueLock) {
4093            if (mFullBackupQueue.size() > 0) {
4094                // schedule the next job at the point in the future when the least-recently
4095                // backed up app comes due for backup again; or immediately if it's already
4096                // due.
4097                long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
4098                long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
4099                final long latency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
4100                        ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
4101                Runnable r = new Runnable() {
4102                    @Override public void run() {
4103                        FullBackupJob.schedule(mContext, latency);
4104                    }
4105                };
4106                mBackupHandler.postDelayed(r, 2500);
4107            } else {
4108                if (DEBUG_SCHEDULING) {
4109                    Slog.i(TAG, "Full backup queue empty; not scheduling");
4110                }
4111            }
4112        }
4113    }
4114
4115    /**
4116     * Enqueue full backup for the given app, with a note about when it last ran.
4117     */
4118    void enqueueFullBackup(String packageName, long lastBackedUp) {
4119        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
4120        synchronized (mQueueLock) {
4121            int N = mFullBackupQueue.size();
4122            // First, sanity check that we aren't adding a duplicate.  Slow but
4123            // straightforward; we'll have at most on the order of a few hundred
4124            // items in this list.
4125            for (int i = N-1; i >= 0; i--) {
4126                final FullBackupEntry e = mFullBackupQueue.get(i);
4127                if (packageName.equals(e.packageName)) {
4128                    if (DEBUG) {
4129                        Slog.w(TAG, "Removing schedule queue dupe of " + packageName);
4130                    }
4131                    mFullBackupQueue.remove(i);
4132                }
4133            }
4134
4135            // This is also slow but easy for modest numbers of apps: work backwards
4136            // from the end of the queue until we find an item whose last backup
4137            // time was before this one, then insert this new entry after it.
4138            int which;
4139            for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
4140                final FullBackupEntry entry = mFullBackupQueue.get(which);
4141                if (entry.lastBackup <= lastBackedUp) {
4142                    mFullBackupQueue.add(which + 1, newEntry);
4143                    break;
4144                }
4145            }
4146            if (which < 0) {
4147                // this one is earlier than any existing one, so prepend
4148                mFullBackupQueue.add(0, newEntry);
4149            }
4150        }
4151        writeFullBackupScheduleAsync();
4152    }
4153
4154    /**
4155     * Conditions are right for a full backup operation, so run one.  The model we use is
4156     * to perform one app backup per scheduled job execution, and to reschedule the job
4157     * with zero latency as long as conditions remain right and we still have work to do.
4158     *
4159     * @return Whether ongoing work will continue.  The return value here will be passed
4160     *         along as the return value to the scheduled job's onStartJob() callback.
4161     */
4162    boolean beginFullBackup(FullBackupJob scheduledJob) {
4163        long now = System.currentTimeMillis();
4164        FullBackupEntry entry = null;
4165
4166        if (!mEnabled || !mProvisioned) {
4167            // Backups are globally disabled, so don't proceed.  We also don't reschedule
4168            // the job driving automatic backups; that job will be scheduled again when
4169            // the user enables backup.
4170            if (MORE_DEBUG) {
4171                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
4172                        + " p=" + mProvisioned + "; ignoring");
4173            }
4174            return false;
4175        }
4176
4177        if (DEBUG_SCHEDULING) {
4178            Slog.i(TAG, "Beginning scheduled full backup operation");
4179        }
4180
4181        // Great; we're able to run full backup jobs now.  See if we have any work to do.
4182        synchronized (mQueueLock) {
4183            if (mRunningFullBackupTask != null) {
4184                Slog.e(TAG, "Backup triggered but one already/still running!");
4185                return false;
4186            }
4187
4188            if (mFullBackupQueue.size() == 0) {
4189                // no work to do so just bow out
4190                if (DEBUG) {
4191                    Slog.i(TAG, "Backup queue empty; doing nothing");
4192                }
4193                return false;
4194            }
4195
4196            entry = mFullBackupQueue.get(0);
4197            long timeSinceRun = now - entry.lastBackup;
4198            if (timeSinceRun < MIN_FULL_BACKUP_INTERVAL) {
4199                // It's too early to back up the next thing in the queue, so bow out
4200                if (MORE_DEBUG) {
4201                    Slog.i(TAG, "Device ready but too early to back up next app");
4202                }
4203                final long latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
4204                mBackupHandler.post(new Runnable() {
4205                    @Override public void run() {
4206                        FullBackupJob.schedule(mContext, latency);
4207                    }
4208                });
4209                return false;
4210            }
4211
4212            // Okay, the top thing is runnable now.  Pop it off and get going.
4213            mFullBackupQueue.remove(0);
4214            AtomicBoolean latch = new AtomicBoolean(false);
4215            String[] pkg = new String[] {entry.packageName};
4216            mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
4217                    scheduledJob, latch);
4218            (new Thread(mRunningFullBackupTask)).start();
4219        }
4220
4221        return true;
4222    }
4223
4224    // The job scheduler says our constraints don't hold any more,
4225    // so tear down any ongoing backup task right away.
4226    void endFullBackup() {
4227        synchronized (mQueueLock) {
4228            if (mRunningFullBackupTask != null) {
4229                if (DEBUG_SCHEDULING) {
4230                    Slog.i(TAG, "Telling running backup to stop");
4231                }
4232                mRunningFullBackupTask.setRunning(false);
4233            }
4234        }
4235    }
4236
4237    // ----- Restore infrastructure -----
4238
4239    abstract class RestoreEngine {
4240        static final String TAG = "RestoreEngine";
4241
4242        public static final int SUCCESS = 0;
4243        public static final int TARGET_FAILURE = -2;
4244        public static final int TRANSPORT_FAILURE = -3;
4245
4246        private AtomicBoolean mRunning = new AtomicBoolean(false);
4247        private AtomicInteger mResult = new AtomicInteger(SUCCESS);
4248
4249        public boolean isRunning() {
4250            return mRunning.get();
4251        }
4252
4253        public void setRunning(boolean stillRunning) {
4254            synchronized (mRunning) {
4255                mRunning.set(stillRunning);
4256                mRunning.notifyAll();
4257            }
4258        }
4259
4260        public int waitForResult() {
4261            synchronized (mRunning) {
4262                while (isRunning()) {
4263                    try {
4264                        mRunning.wait();
4265                    } catch (InterruptedException e) {}
4266                }
4267            }
4268            return getResult();
4269        }
4270
4271        public int getResult() {
4272            return mResult.get();
4273        }
4274
4275        public void setResult(int result) {
4276            mResult.set(result);
4277        }
4278
4279        // TODO: abstract restore state and APIs
4280    }
4281
4282    // ----- Full restore from a file/socket -----
4283
4284    // Description of a file in the restore datastream
4285    static class FileMetadata {
4286        String packageName;             // name of the owning app
4287        String installerPackageName;    // name of the market-type app that installed the owner
4288        int type;                       // e.g. BackupAgent.TYPE_DIRECTORY
4289        String domain;                  // e.g. FullBackup.DATABASE_TREE_TOKEN
4290        String path;                    // subpath within the semantic domain
4291        long mode;                      // e.g. 0666 (actually int)
4292        long mtime;                     // last mod time, UTC time_t (actually int)
4293        long size;                      // bytes of content
4294
4295        @Override
4296        public String toString() {
4297            StringBuilder sb = new StringBuilder(128);
4298            sb.append("FileMetadata{");
4299            sb.append(packageName); sb.append(',');
4300            sb.append(type); sb.append(',');
4301            sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
4302            sb.append(size);
4303            sb.append('}');
4304            return sb.toString();
4305        }
4306    }
4307
4308    enum RestorePolicy {
4309        IGNORE,
4310        ACCEPT,
4311        ACCEPT_IF_APK
4312    }
4313
4314    // Full restore engine, used by both adb restore and transport-based full restore
4315    class FullRestoreEngine extends RestoreEngine {
4316        // Dedicated observer, if any
4317        IFullBackupRestoreObserver mObserver;
4318
4319        // Where we're delivering the file data as we go
4320        IBackupAgent mAgent;
4321
4322        // Are we permitted to only deliver a specific package's metadata?
4323        PackageInfo mOnlyPackage;
4324
4325        boolean mAllowApks;
4326        boolean mAllowObbs;
4327
4328        // Which package are we currently handling data for?
4329        String mAgentPackage;
4330
4331        // Info for working with the target app process
4332        ApplicationInfo mTargetApp;
4333
4334        // Machinery for restoring OBBs
4335        FullBackupObbConnection mObbConnection = null;
4336
4337        // possible handling states for a given package in the restore dataset
4338        final HashMap<String, RestorePolicy> mPackagePolicies
4339                = new HashMap<String, RestorePolicy>();
4340
4341        // installer package names for each encountered app, derived from the manifests
4342        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
4343
4344        // Signatures for a given package found in its manifest file
4345        final HashMap<String, Signature[]> mManifestSignatures
4346                = new HashMap<String, Signature[]>();
4347
4348        // Packages we've already wiped data on when restoring their first file
4349        final HashSet<String> mClearedPackages = new HashSet<String>();
4350
4351        // How much data have we moved?
4352        long mBytes;
4353
4354        // Working buffer
4355        byte[] mBuffer;
4356
4357        // Pipes for moving data
4358        ParcelFileDescriptor[] mPipes = null;
4359
4360        // Widget blob to be restored out-of-band
4361        byte[] mWidgetData = null;
4362
4363        // Runner that can be placed in a separate thread to do in-process
4364        // invocations of the full restore API asynchronously
4365        class RestoreFileRunnable implements Runnable {
4366            IBackupAgent mAgent;
4367            FileMetadata mInfo;
4368            ParcelFileDescriptor mSocket;
4369            int mToken;
4370
4371            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
4372                    ParcelFileDescriptor socket, int token) throws IOException {
4373                mAgent = agent;
4374                mInfo = info;
4375                mToken = token;
4376
4377                // This class is used strictly for process-local binder invocations.  The
4378                // semantics of ParcelFileDescriptor differ in this case; in particular, we
4379                // do not automatically get a 'dup'ed descriptor that we can can continue
4380                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
4381                // before proceeding to do the restore.
4382                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
4383            }
4384
4385            @Override
4386            public void run() {
4387                try {
4388                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
4389                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
4390                            mToken, mBackupManagerBinder);
4391                } catch (RemoteException e) {
4392                    // never happens; this is used strictly for local binder calls
4393                }
4394            }
4395        }
4396
4397        public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage,
4398                boolean allowApks, boolean allowObbs) {
4399            mObserver = observer;
4400            mOnlyPackage = onlyPackage;
4401            mAllowApks = allowApks;
4402            mAllowObbs = allowObbs;
4403            mBuffer = new byte[32 * 1024];
4404            mBytes = 0;
4405        }
4406
4407        public boolean restoreOneFile(InputStream instream) {
4408            if (!isRunning()) {
4409                Slog.w(TAG, "Restore engine used after halting");
4410                return false;
4411            }
4412
4413            FileMetadata info;
4414            try {
4415                if (MORE_DEBUG) {
4416                    Slog.v(TAG, "Reading tar header for restoring file");
4417                }
4418                info = readTarHeaders(instream);
4419                if (info != null) {
4420                    if (MORE_DEBUG) {
4421                        dumpFileMetadata(info);
4422                    }
4423
4424                    final String pkg = info.packageName;
4425                    if (!pkg.equals(mAgentPackage)) {
4426                        // In the single-package case, it's a semantic error to expect
4427                        // one app's data but see a different app's on the wire
4428                        if (mOnlyPackage != null) {
4429                            if (!pkg.equals(mOnlyPackage.packageName)) {
4430                                Slog.w(TAG, "Expected data for " + mOnlyPackage
4431                                        + " but saw " + pkg);
4432                                setResult(RestoreEngine.TRANSPORT_FAILURE);
4433                                setRunning(false);
4434                                return false;
4435                            }
4436                        }
4437
4438                        // okay, change in package; set up our various
4439                        // bookkeeping if we haven't seen it yet
4440                        if (!mPackagePolicies.containsKey(pkg)) {
4441                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4442                        }
4443
4444                        // Clean up the previous agent relationship if necessary,
4445                        // and let the observer know we're considering a new app.
4446                        if (mAgent != null) {
4447                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
4448                            // Now we're really done
4449                            tearDownPipes();
4450                            tearDownAgent(mTargetApp);
4451                            mTargetApp = null;
4452                            mAgentPackage = null;
4453                        }
4454                    }
4455
4456                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
4457                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
4458                        mPackageInstallers.put(pkg, info.installerPackageName);
4459                        // We've read only the manifest content itself at this point,
4460                        // so consume the footer before looping around to the next
4461                        // input file
4462                        skipTarPadding(info.size, instream);
4463                        sendOnRestorePackage(pkg);
4464                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
4465                        // Metadata blobs!
4466                        readMetadata(info, instream);
4467                        skipTarPadding(info.size, instream);
4468                    } else {
4469                        // Non-manifest, so it's actual file data.  Is this a package
4470                        // we're ignoring?
4471                        boolean okay = true;
4472                        RestorePolicy policy = mPackagePolicies.get(pkg);
4473                        switch (policy) {
4474                            case IGNORE:
4475                                okay = false;
4476                                break;
4477
4478                            case ACCEPT_IF_APK:
4479                                // If we're in accept-if-apk state, then the first file we
4480                                // see MUST be the apk.
4481                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
4482                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
4483                                    // Try to install the app.
4484                                    String installerName = mPackageInstallers.get(pkg);
4485                                    okay = installApk(info, installerName, instream);
4486                                    // good to go; promote to ACCEPT
4487                                    mPackagePolicies.put(pkg, (okay)
4488                                            ? RestorePolicy.ACCEPT
4489                                                    : RestorePolicy.IGNORE);
4490                                    // At this point we've consumed this file entry
4491                                    // ourselves, so just strip the tar footer and
4492                                    // go on to the next file in the input stream
4493                                    skipTarPadding(info.size, instream);
4494                                    return true;
4495                                } else {
4496                                    // File data before (or without) the apk.  We can't
4497                                    // handle it coherently in this case so ignore it.
4498                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4499                                    okay = false;
4500                                }
4501                                break;
4502
4503                            case ACCEPT:
4504                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
4505                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
4506                                    // we can take the data without the apk, so we
4507                                    // *want* to do so.  skip the apk by declaring this
4508                                    // one file not-okay without changing the restore
4509                                    // policy for the package.
4510                                    okay = false;
4511                                }
4512                                break;
4513
4514                            default:
4515                                // Something has gone dreadfully wrong when determining
4516                                // the restore policy from the manifest.  Ignore the
4517                                // rest of this package's data.
4518                                Slog.e(TAG, "Invalid policy from manifest");
4519                                okay = false;
4520                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4521                                break;
4522                        }
4523
4524                        // Is it a *file* we need to drop?
4525                        if (!isRestorableFile(info)) {
4526                            okay = false;
4527                        }
4528
4529                        // If the policy is satisfied, go ahead and set up to pipe the
4530                        // data to the agent.
4531                        if (DEBUG && okay && mAgent != null) {
4532                            Slog.i(TAG, "Reusing existing agent instance");
4533                        }
4534                        if (okay && mAgent == null) {
4535                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
4536
4537                            try {
4538                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
4539
4540                                // If we haven't sent any data to this app yet, we probably
4541                                // need to clear it first.  Check that.
4542                                if (!mClearedPackages.contains(pkg)) {
4543                                    // apps with their own backup agents are
4544                                    // responsible for coherently managing a full
4545                                    // restore.
4546                                    if (mTargetApp.backupAgentName == null) {
4547                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
4548                                        clearApplicationDataSynchronous(pkg);
4549                                    } else {
4550                                        if (DEBUG) Slog.d(TAG, "backup agent ("
4551                                                + mTargetApp.backupAgentName + ") => no clear");
4552                                    }
4553                                    mClearedPackages.add(pkg);
4554                                } else {
4555                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
4556                                }
4557
4558                                // All set; now set up the IPC and launch the agent
4559                                setUpPipes();
4560                                mAgent = bindToAgentSynchronous(mTargetApp,
4561                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
4562                                mAgentPackage = pkg;
4563                            } catch (IOException e) {
4564                                // fall through to error handling
4565                            } catch (NameNotFoundException e) {
4566                                // fall through to error handling
4567                            }
4568
4569                            if (mAgent == null) {
4570                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
4571                                okay = false;
4572                                tearDownPipes();
4573                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4574                            }
4575                        }
4576
4577                        // Sanity check: make sure we never give data to the wrong app.  This
4578                        // should never happen but a little paranoia here won't go amiss.
4579                        if (okay && !pkg.equals(mAgentPackage)) {
4580                            Slog.e(TAG, "Restoring data for " + pkg
4581                                    + " but agent is for " + mAgentPackage);
4582                            okay = false;
4583                        }
4584
4585                        // At this point we have an agent ready to handle the full
4586                        // restore data as well as a pipe for sending data to
4587                        // that agent.  Tell the agent to start reading from the
4588                        // pipe.
4589                        if (okay) {
4590                            boolean agentSuccess = true;
4591                            long toCopy = info.size;
4592                            final int token = generateToken();
4593                            try {
4594                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
4595                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
4596                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
4597                                            + " : " + info.path);
4598                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
4599                                            info.size, info.type, info.path, info.mode,
4600                                            info.mtime, token, mBackupManagerBinder);
4601                                } else {
4602                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
4603                                            + info.path);
4604                                    // fire up the app's agent listening on the socket.  If
4605                                    // the agent is running in the system process we can't
4606                                    // just invoke it asynchronously, so we provide a thread
4607                                    // for it here.
4608                                    if (mTargetApp.processName.equals("system")) {
4609                                        Slog.d(TAG, "system process agent - spinning a thread");
4610                                        RestoreFileRunnable runner = new RestoreFileRunnable(
4611                                                mAgent, info, mPipes[0], token);
4612                                        new Thread(runner, "restore-sys-runner").start();
4613                                    } else {
4614                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
4615                                                info.domain, info.path, info.mode, info.mtime,
4616                                                token, mBackupManagerBinder);
4617                                    }
4618                                }
4619                            } catch (IOException e) {
4620                                // couldn't dup the socket for a process-local restore
4621                                Slog.d(TAG, "Couldn't establish restore");
4622                                agentSuccess = false;
4623                                okay = false;
4624                            } catch (RemoteException e) {
4625                                // whoops, remote entity went away.  We'll eat the content
4626                                // ourselves, then, and not copy it over.
4627                                Slog.e(TAG, "Agent crashed during full restore");
4628                                agentSuccess = false;
4629                                okay = false;
4630                            }
4631
4632                            // Copy over the data if the agent is still good
4633                            if (okay) {
4634                                if (MORE_DEBUG) {
4635                                    Slog.v(TAG, "  copying to restore agent: "
4636                                            + toCopy + " bytes");
4637                                }
4638                                boolean pipeOkay = true;
4639                                FileOutputStream pipe = new FileOutputStream(
4640                                        mPipes[1].getFileDescriptor());
4641                                while (toCopy > 0) {
4642                                    int toRead = (toCopy > mBuffer.length)
4643                                            ? mBuffer.length : (int)toCopy;
4644                                    int nRead = instream.read(mBuffer, 0, toRead);
4645                                    if (nRead >= 0) mBytes += nRead;
4646                                    if (nRead <= 0) break;
4647                                    toCopy -= nRead;
4648
4649                                    // send it to the output pipe as long as things
4650                                    // are still good
4651                                    if (pipeOkay) {
4652                                        try {
4653                                            pipe.write(mBuffer, 0, nRead);
4654                                        } catch (IOException e) {
4655                                            Slog.e(TAG, "Failed to write to restore pipe", e);
4656                                            pipeOkay = false;
4657                                        }
4658                                    }
4659                                }
4660
4661                                // done sending that file!  Now we just need to consume
4662                                // the delta from info.size to the end of block.
4663                                skipTarPadding(info.size, instream);
4664
4665                                // and now that we've sent it all, wait for the remote
4666                                // side to acknowledge receipt
4667                                agentSuccess = waitUntilOperationComplete(token);
4668                            }
4669
4670                            // okay, if the remote end failed at any point, deal with
4671                            // it by ignoring the rest of the restore on it
4672                            if (!agentSuccess) {
4673                                if (DEBUG) {
4674                                    Slog.i(TAG, "Agent failure; ending restore");
4675                                }
4676                                mBackupHandler.removeMessages(MSG_TIMEOUT);
4677                                tearDownPipes();
4678                                tearDownAgent(mTargetApp);
4679                                mAgent = null;
4680                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4681
4682                                // If this was a single-package restore, we halt immediately
4683                                // with an agent error under these circumstances
4684                                if (mOnlyPackage != null) {
4685                                    setResult(RestoreEngine.TARGET_FAILURE);
4686                                    setRunning(false);
4687                                    return false;
4688                                }
4689                            }
4690                        }
4691
4692                        // Problems setting up the agent communication, an explicitly
4693                        // dropped file, or an already-ignored package: skip to the
4694                        // next stream entry by reading and discarding this file.
4695                        if (!okay) {
4696                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
4697                            long bytesToConsume = (info.size + 511) & ~511;
4698                            while (bytesToConsume > 0) {
4699                                int toRead = (bytesToConsume > mBuffer.length)
4700                                        ? mBuffer.length : (int)bytesToConsume;
4701                                long nRead = instream.read(mBuffer, 0, toRead);
4702                                if (nRead >= 0) mBytes += nRead;
4703                                if (nRead <= 0) break;
4704                                bytesToConsume -= nRead;
4705                            }
4706                        }
4707                    }
4708                }
4709            } catch (IOException e) {
4710                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
4711                setResult(RestoreEngine.TRANSPORT_FAILURE);
4712                info = null;
4713            }
4714
4715            // If we got here we're either running smoothly or we've finished
4716            if (info == null) {
4717                if (MORE_DEBUG) {
4718                    Slog.i(TAG, "No [more] data for this package; tearing down");
4719                }
4720                tearDownPipes();
4721                tearDownAgent(mTargetApp);
4722                setRunning(false);
4723            }
4724            return (info != null);
4725        }
4726
4727        void setUpPipes() throws IOException {
4728            mPipes = ParcelFileDescriptor.createPipe();
4729        }
4730
4731        void tearDownPipes() {
4732            if (mPipes != null) {
4733                try {
4734                    mPipes[0].close();
4735                    mPipes[0] = null;
4736                    mPipes[1].close();
4737                    mPipes[1] = null;
4738                } catch (IOException e) {
4739                    Slog.w(TAG, "Couldn't close agent pipes", e);
4740                }
4741                mPipes = null;
4742            }
4743        }
4744
4745        void tearDownAgent(ApplicationInfo app) {
4746            if (mAgent != null) {
4747                try {
4748                    // unbind and tidy up even on timeout or failure, just in case
4749                    mActivityManager.unbindBackupAgent(app);
4750
4751                    // The agent was running with a stub Application object, so shut it down.
4752                    // !!! We hardcode the confirmation UI's package name here rather than use a
4753                    //     manifest flag!  TODO something less direct.
4754                    if (app.uid != Process.SYSTEM_UID
4755                            && !app.packageName.equals("com.android.backupconfirm")) {
4756                        if (DEBUG) Slog.d(TAG, "Killing host process");
4757                        mActivityManager.killApplicationProcess(app.processName, app.uid);
4758                    } else {
4759                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
4760                    }
4761                } catch (RemoteException e) {
4762                    Slog.d(TAG, "Lost app trying to shut down");
4763                }
4764                mAgent = null;
4765            }
4766        }
4767
4768        class RestoreInstallObserver extends IPackageInstallObserver.Stub {
4769            final AtomicBoolean mDone = new AtomicBoolean();
4770            String mPackageName;
4771            int mResult;
4772
4773            public void reset() {
4774                synchronized (mDone) {
4775                    mDone.set(false);
4776                }
4777            }
4778
4779            public void waitForCompletion() {
4780                synchronized (mDone) {
4781                    while (mDone.get() == false) {
4782                        try {
4783                            mDone.wait();
4784                        } catch (InterruptedException e) { }
4785                    }
4786                }
4787            }
4788
4789            int getResult() {
4790                return mResult;
4791            }
4792
4793            @Override
4794            public void packageInstalled(String packageName, int returnCode)
4795                    throws RemoteException {
4796                synchronized (mDone) {
4797                    mResult = returnCode;
4798                    mPackageName = packageName;
4799                    mDone.set(true);
4800                    mDone.notifyAll();
4801                }
4802            }
4803        }
4804
4805        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
4806            final AtomicBoolean mDone = new AtomicBoolean();
4807            int mResult;
4808
4809            public void reset() {
4810                synchronized (mDone) {
4811                    mDone.set(false);
4812                }
4813            }
4814
4815            public void waitForCompletion() {
4816                synchronized (mDone) {
4817                    while (mDone.get() == false) {
4818                        try {
4819                            mDone.wait();
4820                        } catch (InterruptedException e) { }
4821                    }
4822                }
4823            }
4824
4825            @Override
4826            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
4827                synchronized (mDone) {
4828                    mResult = returnCode;
4829                    mDone.set(true);
4830                    mDone.notifyAll();
4831                }
4832            }
4833        }
4834
4835        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
4836        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
4837
4838        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
4839            boolean okay = true;
4840
4841            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
4842
4843            // The file content is an .apk file.  Copy it out to a staging location and
4844            // attempt to install it.
4845            File apkFile = new File(mDataDir, info.packageName);
4846            try {
4847                FileOutputStream apkStream = new FileOutputStream(apkFile);
4848                byte[] buffer = new byte[32 * 1024];
4849                long size = info.size;
4850                while (size > 0) {
4851                    long toRead = (buffer.length < size) ? buffer.length : size;
4852                    int didRead = instream.read(buffer, 0, (int)toRead);
4853                    if (didRead >= 0) mBytes += didRead;
4854                    apkStream.write(buffer, 0, didRead);
4855                    size -= didRead;
4856                }
4857                apkStream.close();
4858
4859                // make sure the installer can read it
4860                apkFile.setReadable(true, false);
4861
4862                // Now install it
4863                Uri packageUri = Uri.fromFile(apkFile);
4864                mInstallObserver.reset();
4865                mPackageManager.installPackage(packageUri, mInstallObserver,
4866                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
4867                        installerPackage);
4868                mInstallObserver.waitForCompletion();
4869
4870                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
4871                    // The only time we continue to accept install of data even if the
4872                    // apk install failed is if we had already determined that we could
4873                    // accept the data regardless.
4874                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
4875                        okay = false;
4876                    }
4877                } else {
4878                    // Okay, the install succeeded.  Make sure it was the right app.
4879                    boolean uninstall = false;
4880                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
4881                        Slog.w(TAG, "Restore stream claimed to include apk for "
4882                                + info.packageName + " but apk was really "
4883                                + mInstallObserver.mPackageName);
4884                        // delete the package we just put in place; it might be fraudulent
4885                        okay = false;
4886                        uninstall = true;
4887                    } else {
4888                        try {
4889                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
4890                                    PackageManager.GET_SIGNATURES);
4891                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
4892                                Slog.w(TAG, "Restore stream contains apk of package "
4893                                        + info.packageName + " but it disallows backup/restore");
4894                                okay = false;
4895                            } else {
4896                                // So far so good -- do the signatures match the manifest?
4897                                Signature[] sigs = mManifestSignatures.get(info.packageName);
4898                                if (signaturesMatch(sigs, pkg)) {
4899                                    // If this is a system-uid app without a declared backup agent,
4900                                    // don't restore any of the file data.
4901                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
4902                                            && (pkg.applicationInfo.backupAgentName == null)) {
4903                                        Slog.w(TAG, "Installed app " + info.packageName
4904                                                + " has restricted uid and no agent");
4905                                        okay = false;
4906                                    }
4907                                } else {
4908                                    Slog.w(TAG, "Installed app " + info.packageName
4909                                            + " signatures do not match restore manifest");
4910                                    okay = false;
4911                                    uninstall = true;
4912                                }
4913                            }
4914                        } catch (NameNotFoundException e) {
4915                            Slog.w(TAG, "Install of package " + info.packageName
4916                                    + " succeeded but now not found");
4917                            okay = false;
4918                        }
4919                    }
4920
4921                    // If we're not okay at this point, we need to delete the package
4922                    // that we just installed.
4923                    if (uninstall) {
4924                        mDeleteObserver.reset();
4925                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
4926                                mDeleteObserver, 0);
4927                        mDeleteObserver.waitForCompletion();
4928                    }
4929                }
4930            } catch (IOException e) {
4931                Slog.e(TAG, "Unable to transcribe restored apk for install");
4932                okay = false;
4933            } finally {
4934                apkFile.delete();
4935            }
4936
4937            return okay;
4938        }
4939
4940        // Given an actual file content size, consume the post-content padding mandated
4941        // by the tar format.
4942        void skipTarPadding(long size, InputStream instream) throws IOException {
4943            long partial = (size + 512) % 512;
4944            if (partial > 0) {
4945                final int needed = 512 - (int)partial;
4946                if (MORE_DEBUG) {
4947                    Slog.i(TAG, "Skipping tar padding: " + needed + " bytes");
4948                }
4949                byte[] buffer = new byte[needed];
4950                if (readExactly(instream, buffer, 0, needed) == needed) {
4951                    mBytes += needed;
4952                } else throw new IOException("Unexpected EOF in padding");
4953            }
4954        }
4955
4956        // Read a widget metadata file, returning the restored blob
4957        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
4958            // Fail on suspiciously large widget dump files
4959            if (info.size > 64 * 1024) {
4960                throw new IOException("Metadata too big; corrupt? size=" + info.size);
4961            }
4962
4963            byte[] buffer = new byte[(int) info.size];
4964            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
4965                mBytes += info.size;
4966            } else throw new IOException("Unexpected EOF in widget data");
4967
4968            String[] str = new String[1];
4969            int offset = extractLine(buffer, 0, str);
4970            int version = Integer.parseInt(str[0]);
4971            if (version == BACKUP_MANIFEST_VERSION) {
4972                offset = extractLine(buffer, offset, str);
4973                final String pkg = str[0];
4974                if (info.packageName.equals(pkg)) {
4975                    // Data checks out -- the rest of the buffer is a concatenation of
4976                    // binary blobs as described in the comment at writeAppWidgetData()
4977                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
4978                            offset, buffer.length - offset);
4979                    DataInputStream in = new DataInputStream(bin);
4980                    while (bin.available() > 0) {
4981                        int token = in.readInt();
4982                        int size = in.readInt();
4983                        if (size > 64 * 1024) {
4984                            throw new IOException("Datum "
4985                                    + Integer.toHexString(token)
4986                                    + " too big; corrupt? size=" + info.size);
4987                        }
4988                        switch (token) {
4989                            case BACKUP_WIDGET_METADATA_TOKEN:
4990                            {
4991                                if (MORE_DEBUG) {
4992                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
4993                                }
4994                                mWidgetData = new byte[size];
4995                                in.read(mWidgetData);
4996                                break;
4997                            }
4998                            default:
4999                            {
5000                                if (DEBUG) {
5001                                    Slog.i(TAG, "Ignoring metadata blob "
5002                                            + Integer.toHexString(token)
5003                                            + " for " + info.packageName);
5004                                }
5005                                in.skipBytes(size);
5006                                break;
5007                            }
5008                        }
5009                    }
5010                } else {
5011                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
5012                            + " but widget data for " + pkg);
5013                }
5014            } else {
5015                Slog.w(TAG, "Unsupported metadata version " + version);
5016            }
5017        }
5018
5019        // Returns a policy constant
5020        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
5021                throws IOException {
5022            // Fail on suspiciously large manifest files
5023            if (info.size > 64 * 1024) {
5024                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
5025            }
5026
5027            byte[] buffer = new byte[(int) info.size];
5028            if (MORE_DEBUG) {
5029                Slog.i(TAG, "   readAppManifest() looking for " + info.size + " bytes, "
5030                        + mBytes + " already consumed");
5031            }
5032            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5033                mBytes += info.size;
5034            } else throw new IOException("Unexpected EOF in manifest");
5035
5036            RestorePolicy policy = RestorePolicy.IGNORE;
5037            String[] str = new String[1];
5038            int offset = 0;
5039
5040            try {
5041                offset = extractLine(buffer, offset, str);
5042                int version = Integer.parseInt(str[0]);
5043                if (version == BACKUP_MANIFEST_VERSION) {
5044                    offset = extractLine(buffer, offset, str);
5045                    String manifestPackage = str[0];
5046                    // TODO: handle <original-package>
5047                    if (manifestPackage.equals(info.packageName)) {
5048                        offset = extractLine(buffer, offset, str);
5049                        version = Integer.parseInt(str[0]);  // app version
5050                        offset = extractLine(buffer, offset, str);
5051                        int platformVersion = Integer.parseInt(str[0]);
5052                        offset = extractLine(buffer, offset, str);
5053                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
5054                        offset = extractLine(buffer, offset, str);
5055                        boolean hasApk = str[0].equals("1");
5056                        offset = extractLine(buffer, offset, str);
5057                        int numSigs = Integer.parseInt(str[0]);
5058                        if (numSigs > 0) {
5059                            Signature[] sigs = new Signature[numSigs];
5060                            for (int i = 0; i < numSigs; i++) {
5061                                offset = extractLine(buffer, offset, str);
5062                                sigs[i] = new Signature(str[0]);
5063                            }
5064                            mManifestSignatures.put(info.packageName, sigs);
5065
5066                            // Okay, got the manifest info we need...
5067                            try {
5068                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
5069                                        info.packageName, PackageManager.GET_SIGNATURES);
5070                                // Fall through to IGNORE if the app explicitly disallows backup
5071                                final int flags = pkgInfo.applicationInfo.flags;
5072                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
5073                                    // Restore system-uid-space packages only if they have
5074                                    // defined a custom backup agent
5075                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
5076                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
5077                                        // Verify signatures against any installed version; if they
5078                                        // don't match, then we fall though and ignore the data.  The
5079                                        // signatureMatch() method explicitly ignores the signature
5080                                        // check for packages installed on the system partition, because
5081                                        // such packages are signed with the platform cert instead of
5082                                        // the app developer's cert, so they're different on every
5083                                        // device.
5084                                        if (signaturesMatch(sigs, pkgInfo)) {
5085                                            if (pkgInfo.versionCode >= version) {
5086                                                Slog.i(TAG, "Sig + version match; taking data");
5087                                                policy = RestorePolicy.ACCEPT;
5088                                            } else {
5089                                                // The data is from a newer version of the app than
5090                                                // is presently installed.  That means we can only
5091                                                // use it if the matching apk is also supplied.
5092                                                if (mAllowApks) {
5093                                                    Slog.i(TAG, "Data version " + version
5094                                                            + " is newer than installed version "
5095                                                            + pkgInfo.versionCode
5096                                                            + " - requiring apk");
5097                                                    policy = RestorePolicy.ACCEPT_IF_APK;
5098                                                } else {
5099                                                    Slog.i(TAG, "Data requires newer version "
5100                                                            + version + "; ignoring");
5101                                                    policy = RestorePolicy.IGNORE;
5102                                                }
5103                                            }
5104                                        } else {
5105                                            Slog.w(TAG, "Restore manifest signatures do not match "
5106                                                    + "installed application for " + info.packageName);
5107                                        }
5108                                    } else {
5109                                        Slog.w(TAG, "Package " + info.packageName
5110                                                + " is system level with no agent");
5111                                    }
5112                                } else {
5113                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
5114                                            + info.packageName + " but allowBackup=false");
5115                                }
5116                            } catch (NameNotFoundException e) {
5117                                // Okay, the target app isn't installed.  We can process
5118                                // the restore properly only if the dataset provides the
5119                                // apk file and we can successfully install it.
5120                                if (mAllowApks) {
5121                                    if (DEBUG) Slog.i(TAG, "Package " + info.packageName
5122                                            + " not installed; requiring apk in dataset");
5123                                    policy = RestorePolicy.ACCEPT_IF_APK;
5124                                } else {
5125                                    policy = RestorePolicy.IGNORE;
5126                                }
5127                            }
5128
5129                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
5130                                Slog.i(TAG, "Cannot restore package " + info.packageName
5131                                        + " without the matching .apk");
5132                            }
5133                        } else {
5134                            Slog.i(TAG, "Missing signature on backed-up package "
5135                                    + info.packageName);
5136                        }
5137                    } else {
5138                        Slog.i(TAG, "Expected package " + info.packageName
5139                                + " but restore manifest claims " + manifestPackage);
5140                    }
5141                } else {
5142                    Slog.i(TAG, "Unknown restore manifest version " + version
5143                            + " for package " + info.packageName);
5144                }
5145            } catch (NumberFormatException e) {
5146                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
5147            } catch (IllegalArgumentException e) {
5148                Slog.w(TAG, e.getMessage());
5149            }
5150
5151            return policy;
5152        }
5153
5154        // Builds a line from a byte buffer starting at 'offset', and returns
5155        // the index of the next unconsumed data in the buffer.
5156        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
5157            final int end = buffer.length;
5158            if (offset >= end) throw new IOException("Incomplete data");
5159
5160            int pos;
5161            for (pos = offset; pos < end; pos++) {
5162                byte c = buffer[pos];
5163                // at LF we declare end of line, and return the next char as the
5164                // starting point for the next time through
5165                if (c == '\n') {
5166                    break;
5167                }
5168            }
5169            outStr[0] = new String(buffer, offset, pos - offset);
5170            pos++;  // may be pointing an extra byte past the end but that's okay
5171            return pos;
5172        }
5173
5174        void dumpFileMetadata(FileMetadata info) {
5175            if (DEBUG) {
5176                StringBuilder b = new StringBuilder(128);
5177
5178                // mode string
5179                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
5180                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
5181                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
5182                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
5183                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
5184                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
5185                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
5186                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
5187                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
5188                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
5189                b.append(String.format(" %9d ", info.size));
5190
5191                Date stamp = new Date(info.mtime);
5192                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
5193
5194                b.append(info.packageName);
5195                b.append(" :: ");
5196                b.append(info.domain);
5197                b.append(" :: ");
5198                b.append(info.path);
5199
5200                Slog.i(TAG, b.toString());
5201            }
5202        }
5203
5204        // Consume a tar file header block [sequence] and accumulate the relevant metadata
5205        FileMetadata readTarHeaders(InputStream instream) throws IOException {
5206            byte[] block = new byte[512];
5207            FileMetadata info = null;
5208
5209            boolean gotHeader = readTarHeader(instream, block);
5210            if (gotHeader) {
5211                try {
5212                    // okay, presume we're okay, and extract the various metadata
5213                    info = new FileMetadata();
5214                    info.size = extractRadix(block, 124, 12, 8);
5215                    info.mtime = extractRadix(block, 136, 12, 8);
5216                    info.mode = extractRadix(block, 100, 8, 8);
5217
5218                    info.path = extractString(block, 345, 155); // prefix
5219                    String path = extractString(block, 0, 100);
5220                    if (path.length() > 0) {
5221                        if (info.path.length() > 0) info.path += '/';
5222                        info.path += path;
5223                    }
5224
5225                    // tar link indicator field: 1 byte at offset 156 in the header.
5226                    int typeChar = block[156];
5227                    if (typeChar == 'x') {
5228                        // pax extended header, so we need to read that
5229                        gotHeader = readPaxExtendedHeader(instream, info);
5230                        if (gotHeader) {
5231                            // and after a pax extended header comes another real header -- read
5232                            // that to find the real file type
5233                            gotHeader = readTarHeader(instream, block);
5234                        }
5235                        if (!gotHeader) throw new IOException("Bad or missing pax header");
5236
5237                        typeChar = block[156];
5238                    }
5239
5240                    switch (typeChar) {
5241                        case '0': info.type = BackupAgent.TYPE_FILE; break;
5242                        case '5': {
5243                            info.type = BackupAgent.TYPE_DIRECTORY;
5244                            if (info.size != 0) {
5245                                Slog.w(TAG, "Directory entry with nonzero size in header");
5246                                info.size = 0;
5247                            }
5248                            break;
5249                        }
5250                        case 0: {
5251                            // presume EOF
5252                            if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
5253                            return null;
5254                        }
5255                        default: {
5256                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
5257                            throw new IOException("Unknown entity type " + typeChar);
5258                        }
5259                    }
5260
5261                    // Parse out the path
5262                    //
5263                    // first: apps/shared/unrecognized
5264                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
5265                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
5266                        // File in shared storage.  !!! TODO: implement this.
5267                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
5268                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
5269                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
5270                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
5271                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
5272                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
5273                        // App content!  Parse out the package name and domain
5274
5275                        // strip the apps/ prefix
5276                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
5277
5278                        // extract the package name
5279                        int slash = info.path.indexOf('/');
5280                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
5281                        info.packageName = info.path.substring(0, slash);
5282                        info.path = info.path.substring(slash+1);
5283
5284                        // if it's a manifest or metadata payload we're done, otherwise parse
5285                        // out the domain into which the file will be restored
5286                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
5287                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
5288                            slash = info.path.indexOf('/');
5289                            if (slash < 0) {
5290                                throw new IOException("Illegal semantic path in non-manifest "
5291                                        + info.path);
5292                            }
5293                            info.domain = info.path.substring(0, slash);
5294                            info.path = info.path.substring(slash + 1);
5295                        }
5296                    }
5297                } catch (IOException e) {
5298                    if (DEBUG) {
5299                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
5300                        HEXLOG(block);
5301                    }
5302                    throw e;
5303                }
5304            }
5305            return info;
5306        }
5307
5308        private boolean isRestorableFile(FileMetadata info) {
5309            if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
5310                if (MORE_DEBUG) {
5311                    Slog.i(TAG, "Dropping cache file path " + info.path);
5312                }
5313                return false;
5314            }
5315
5316            if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
5317                // It's possible this is "no-backup" dir contents in an archive stream
5318                // produced on a device running a version of the OS that predates that
5319                // API.  Respect the no-backup intention and don't let the data get to
5320                // the app.
5321                if (info.path.startsWith("no_backup/")) {
5322                    if (MORE_DEBUG) {
5323                        Slog.i(TAG, "Dropping no_backup file path " + info.path);
5324                    }
5325                    return false;
5326                }
5327            }
5328
5329            // The path needs to be canonical
5330            if (info.path.contains("..") || info.path.contains("//")) {
5331                if (MORE_DEBUG) {
5332                    Slog.w(TAG, "Dropping invalid path " + info.path);
5333                }
5334                return false;
5335            }
5336
5337            // Otherwise we think this file is good to go
5338            return true;
5339        }
5340
5341        private void HEXLOG(byte[] block) {
5342            int offset = 0;
5343            int todo = block.length;
5344            StringBuilder buf = new StringBuilder(64);
5345            while (todo > 0) {
5346                buf.append(String.format("%04x   ", offset));
5347                int numThisLine = (todo > 16) ? 16 : todo;
5348                for (int i = 0; i < numThisLine; i++) {
5349                    buf.append(String.format("%02x ", block[offset+i]));
5350                }
5351                Slog.i("hexdump", buf.toString());
5352                buf.setLength(0);
5353                todo -= numThisLine;
5354                offset += numThisLine;
5355            }
5356        }
5357
5358        // Read exactly the given number of bytes into a buffer at the stated offset.
5359        // Returns false if EOF is encountered before the requested number of bytes
5360        // could be read.
5361        int readExactly(InputStream in, byte[] buffer, int offset, int size)
5362                throws IOException {
5363            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
5364if (MORE_DEBUG) Slog.i(TAG, "  ... readExactly(" + size + ") called");
5365            int soFar = 0;
5366            while (soFar < size) {
5367                int nRead = in.read(buffer, offset + soFar, size - soFar);
5368                if (nRead <= 0) {
5369                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
5370                    break;
5371                }
5372                soFar += nRead;
5373if (MORE_DEBUG) Slog.v(TAG, "   + got " + nRead + "; now wanting " + (size - soFar));
5374            }
5375            return soFar;
5376        }
5377
5378        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
5379            final int got = readExactly(instream, block, 0, 512);
5380            if (got == 0) return false;     // Clean EOF
5381            if (got < 512) throw new IOException("Unable to read full block header");
5382            mBytes += 512;
5383            return true;
5384        }
5385
5386        // overwrites 'info' fields based on the pax extended header
5387        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
5388                throws IOException {
5389            // We should never see a pax extended header larger than this
5390            if (info.size > 32*1024) {
5391                Slog.w(TAG, "Suspiciously large pax header size " + info.size
5392                        + " - aborting");
5393                throw new IOException("Sanity failure: pax header size " + info.size);
5394            }
5395
5396            // read whole blocks, not just the content size
5397            int numBlocks = (int)((info.size + 511) >> 9);
5398            byte[] data = new byte[numBlocks * 512];
5399            if (readExactly(instream, data, 0, data.length) < data.length) {
5400                throw new IOException("Unable to read full pax header");
5401            }
5402            mBytes += data.length;
5403
5404            final int contentSize = (int) info.size;
5405            int offset = 0;
5406            do {
5407                // extract the line at 'offset'
5408                int eol = offset+1;
5409                while (eol < contentSize && data[eol] != ' ') eol++;
5410                if (eol >= contentSize) {
5411                    // error: we just hit EOD looking for the end of the size field
5412                    throw new IOException("Invalid pax data");
5413                }
5414                // eol points to the space between the count and the key
5415                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
5416                int key = eol + 1;  // start of key=value
5417                eol = offset + linelen - 1; // trailing LF
5418                int value;
5419                for (value = key+1; data[value] != '=' && value <= eol; value++);
5420                if (value > eol) {
5421                    throw new IOException("Invalid pax declaration");
5422                }
5423
5424                // pax requires that key/value strings be in UTF-8
5425                String keyStr = new String(data, key, value-key, "UTF-8");
5426                // -1 to strip the trailing LF
5427                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
5428
5429                if ("path".equals(keyStr)) {
5430                    info.path = valStr;
5431                } else if ("size".equals(keyStr)) {
5432                    info.size = Long.parseLong(valStr);
5433                } else {
5434                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
5435                }
5436
5437                offset += linelen;
5438            } while (offset < contentSize);
5439
5440            return true;
5441        }
5442
5443        long extractRadix(byte[] data, int offset, int maxChars, int radix)
5444                throws IOException {
5445            long value = 0;
5446            final int end = offset + maxChars;
5447            for (int i = offset; i < end; i++) {
5448                final byte b = data[i];
5449                // Numeric fields in tar can terminate with either NUL or SPC
5450                if (b == 0 || b == ' ') break;
5451                if (b < '0' || b > ('0' + radix - 1)) {
5452                    throw new IOException("Invalid number in header: '" + (char)b
5453                            + "' for radix " + radix);
5454                }
5455                value = radix * value + (b - '0');
5456            }
5457            return value;
5458        }
5459
5460        String extractString(byte[] data, int offset, int maxChars) throws IOException {
5461            final int end = offset + maxChars;
5462            int eos = offset;
5463            // tar string fields terminate early with a NUL
5464            while (eos < end && data[eos] != 0) eos++;
5465            return new String(data, offset, eos-offset, "US-ASCII");
5466        }
5467
5468        void sendStartRestore() {
5469            if (mObserver != null) {
5470                try {
5471                    mObserver.onStartRestore();
5472                } catch (RemoteException e) {
5473                    Slog.w(TAG, "full restore observer went away: startRestore");
5474                    mObserver = null;
5475                }
5476            }
5477        }
5478
5479        void sendOnRestorePackage(String name) {
5480            if (mObserver != null) {
5481                try {
5482                    // TODO: use a more user-friendly name string
5483                    mObserver.onRestorePackage(name);
5484                } catch (RemoteException e) {
5485                    Slog.w(TAG, "full restore observer went away: restorePackage");
5486                    mObserver = null;
5487                }
5488            }
5489        }
5490
5491        void sendEndRestore() {
5492            if (mObserver != null) {
5493                try {
5494                    mObserver.onEndRestore();
5495                } catch (RemoteException e) {
5496                    Slog.w(TAG, "full restore observer went away: endRestore");
5497                    mObserver = null;
5498                }
5499            }
5500        }
5501    }
5502
5503    // ***** end new engine class ***
5504
5505    class PerformAdbRestoreTask implements Runnable {
5506        ParcelFileDescriptor mInputFile;
5507        String mCurrentPassword;
5508        String mDecryptPassword;
5509        IFullBackupRestoreObserver mObserver;
5510        AtomicBoolean mLatchObject;
5511        IBackupAgent mAgent;
5512        String mAgentPackage;
5513        ApplicationInfo mTargetApp;
5514        FullBackupObbConnection mObbConnection = null;
5515        ParcelFileDescriptor[] mPipes = null;
5516        byte[] mWidgetData = null;
5517
5518        long mBytes;
5519
5520        // possible handling states for a given package in the restore dataset
5521        final HashMap<String, RestorePolicy> mPackagePolicies
5522                = new HashMap<String, RestorePolicy>();
5523
5524        // installer package names for each encountered app, derived from the manifests
5525        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
5526
5527        // Signatures for a given package found in its manifest file
5528        final HashMap<String, Signature[]> mManifestSignatures
5529                = new HashMap<String, Signature[]>();
5530
5531        // Packages we've already wiped data on when restoring their first file
5532        final HashSet<String> mClearedPackages = new HashSet<String>();
5533
5534        PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
5535                IFullBackupRestoreObserver observer, AtomicBoolean latch) {
5536            mInputFile = fd;
5537            mCurrentPassword = curPassword;
5538            mDecryptPassword = decryptPassword;
5539            mObserver = observer;
5540            mLatchObject = latch;
5541            mAgent = null;
5542            mAgentPackage = null;
5543            mTargetApp = null;
5544            mObbConnection = new FullBackupObbConnection();
5545
5546            // Which packages we've already wiped data on.  We prepopulate this
5547            // with a whitelist of packages known to be unclearable.
5548            mClearedPackages.add("android");
5549            mClearedPackages.add(SETTINGS_PACKAGE);
5550        }
5551
5552        class RestoreFileRunnable implements Runnable {
5553            IBackupAgent mAgent;
5554            FileMetadata mInfo;
5555            ParcelFileDescriptor mSocket;
5556            int mToken;
5557
5558            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
5559                    ParcelFileDescriptor socket, int token) throws IOException {
5560                mAgent = agent;
5561                mInfo = info;
5562                mToken = token;
5563
5564                // This class is used strictly for process-local binder invocations.  The
5565                // semantics of ParcelFileDescriptor differ in this case; in particular, we
5566                // do not automatically get a 'dup'ed descriptor that we can can continue
5567                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
5568                // before proceeding to do the restore.
5569                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
5570            }
5571
5572            @Override
5573            public void run() {
5574                try {
5575                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
5576                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
5577                            mToken, mBackupManagerBinder);
5578                } catch (RemoteException e) {
5579                    // never happens; this is used strictly for local binder calls
5580                }
5581            }
5582        }
5583
5584        @Override
5585        public void run() {
5586            Slog.i(TAG, "--- Performing full-dataset restore ---");
5587            mObbConnection.establish();
5588            sendStartRestore();
5589
5590            // Are we able to restore shared-storage data?
5591            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
5592                mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
5593            }
5594
5595            FileInputStream rawInStream = null;
5596            DataInputStream rawDataIn = null;
5597            try {
5598                if (!backupPasswordMatches(mCurrentPassword)) {
5599                    if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
5600                    return;
5601                }
5602
5603                mBytes = 0;
5604                byte[] buffer = new byte[32 * 1024];
5605                rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
5606                rawDataIn = new DataInputStream(rawInStream);
5607
5608                // First, parse out the unencrypted/uncompressed header
5609                boolean compressed = false;
5610                InputStream preCompressStream = rawInStream;
5611                final InputStream in;
5612
5613                boolean okay = false;
5614                final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
5615                byte[] streamHeader = new byte[headerLen];
5616                rawDataIn.readFully(streamHeader);
5617                byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
5618                if (Arrays.equals(magicBytes, streamHeader)) {
5619                    // okay, header looks good.  now parse out the rest of the fields.
5620                    String s = readHeaderLine(rawInStream);
5621                    final int archiveVersion = Integer.parseInt(s);
5622                    if (archiveVersion <= BACKUP_FILE_VERSION) {
5623                        // okay, it's a version we recognize.  if it's version 1, we may need
5624                        // to try two different PBKDF2 regimes to compare checksums.
5625                        final boolean pbkdf2Fallback = (archiveVersion == 1);
5626
5627                        s = readHeaderLine(rawInStream);
5628                        compressed = (Integer.parseInt(s) != 0);
5629                        s = readHeaderLine(rawInStream);
5630                        if (s.equals("none")) {
5631                            // no more header to parse; we're good to go
5632                            okay = true;
5633                        } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
5634                            preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
5635                                    rawInStream);
5636                            if (preCompressStream != null) {
5637                                okay = true;
5638                            }
5639                        } else Slog.w(TAG, "Archive is encrypted but no password given");
5640                    } else Slog.w(TAG, "Wrong header version: " + s);
5641                } else Slog.w(TAG, "Didn't read the right header magic");
5642
5643                if (!okay) {
5644                    Slog.w(TAG, "Invalid restore data; aborting.");
5645                    return;
5646                }
5647
5648                // okay, use the right stream layer based on compression
5649                in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
5650
5651                boolean didRestore;
5652                do {
5653                    didRestore = restoreOneFile(in, buffer);
5654                } while (didRestore);
5655
5656                if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
5657            } catch (IOException e) {
5658                Slog.e(TAG, "Unable to read restore input");
5659            } finally {
5660                tearDownPipes();
5661                tearDownAgent(mTargetApp);
5662
5663                try {
5664                    if (rawDataIn != null) rawDataIn.close();
5665                    if (rawInStream != null) rawInStream.close();
5666                    mInputFile.close();
5667                } catch (IOException e) {
5668                    Slog.w(TAG, "Close of restore data pipe threw", e);
5669                    /* nothing we can do about this */
5670                }
5671                synchronized (mCurrentOpLock) {
5672                    mCurrentOperations.clear();
5673                }
5674                synchronized (mLatchObject) {
5675                    mLatchObject.set(true);
5676                    mLatchObject.notifyAll();
5677                }
5678                mObbConnection.tearDown();
5679                sendEndRestore();
5680                Slog.d(TAG, "Full restore pass complete.");
5681                mWakelock.release();
5682            }
5683        }
5684
5685        String readHeaderLine(InputStream in) throws IOException {
5686            int c;
5687            StringBuilder buffer = new StringBuilder(80);
5688            while ((c = in.read()) >= 0) {
5689                if (c == '\n') break;   // consume and discard the newlines
5690                buffer.append((char)c);
5691            }
5692            return buffer.toString();
5693        }
5694
5695        InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
5696                int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
5697                boolean doLog) {
5698            InputStream result = null;
5699
5700            try {
5701                Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
5702                SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt,
5703                        rounds);
5704                byte[] IV = hexToByteArray(userIvHex);
5705                IvParameterSpec ivSpec = new IvParameterSpec(IV);
5706                c.init(Cipher.DECRYPT_MODE,
5707                        new SecretKeySpec(userKey.getEncoded(), "AES"),
5708                        ivSpec);
5709                byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
5710                byte[] mkBlob = c.doFinal(mkCipher);
5711
5712                // first, the master key IV
5713                int offset = 0;
5714                int len = mkBlob[offset++];
5715                IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
5716                offset += len;
5717                // then the master key itself
5718                len = mkBlob[offset++];
5719                byte[] mk = Arrays.copyOfRange(mkBlob,
5720                        offset, offset + len);
5721                offset += len;
5722                // and finally the master key checksum hash
5723                len = mkBlob[offset++];
5724                byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
5725                        offset, offset + len);
5726
5727                // now validate the decrypted master key against the checksum
5728                byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds);
5729                if (Arrays.equals(calculatedCk, mkChecksum)) {
5730                    ivSpec = new IvParameterSpec(IV);
5731                    c.init(Cipher.DECRYPT_MODE,
5732                            new SecretKeySpec(mk, "AES"),
5733                            ivSpec);
5734                    // Only if all of the above worked properly will 'result' be assigned
5735                    result = new CipherInputStream(rawInStream, c);
5736                } else if (doLog) Slog.w(TAG, "Incorrect password");
5737            } catch (InvalidAlgorithmParameterException e) {
5738                if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e);
5739            } catch (BadPaddingException e) {
5740                // This case frequently occurs when the wrong password is used to decrypt
5741                // the master key.  Use the identical "incorrect password" log text as is
5742                // used in the checksum failure log in order to avoid providing additional
5743                // information to an attacker.
5744                if (doLog) Slog.w(TAG, "Incorrect password");
5745            } catch (IllegalBlockSizeException e) {
5746                if (doLog) Slog.w(TAG, "Invalid block size in master key");
5747            } catch (NoSuchAlgorithmException e) {
5748                if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!");
5749            } catch (NoSuchPaddingException e) {
5750                if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!");
5751            } catch (InvalidKeyException e) {
5752                if (doLog) Slog.w(TAG, "Illegal password; aborting");
5753            }
5754
5755            return result;
5756        }
5757
5758        InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
5759                InputStream rawInStream) {
5760            InputStream result = null;
5761            try {
5762                if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
5763
5764                    String userSaltHex = readHeaderLine(rawInStream); // 5
5765                    byte[] userSalt = hexToByteArray(userSaltHex);
5766
5767                    String ckSaltHex = readHeaderLine(rawInStream); // 6
5768                    byte[] ckSalt = hexToByteArray(ckSaltHex);
5769
5770                    int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
5771                    String userIvHex = readHeaderLine(rawInStream); // 8
5772
5773                    String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
5774
5775                    // decrypt the master key blob
5776                    result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt,
5777                            rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
5778                    if (result == null && pbkdf2Fallback) {
5779                        result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt,
5780                                rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
5781                    }
5782                } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
5783            } catch (NumberFormatException e) {
5784                Slog.w(TAG, "Can't parse restore data header");
5785            } catch (IOException e) {
5786                Slog.w(TAG, "Can't read input header");
5787            }
5788
5789            return result;
5790        }
5791
5792        boolean restoreOneFile(InputStream instream, byte[] buffer) {
5793            FileMetadata info;
5794            try {
5795                info = readTarHeaders(instream);
5796                if (info != null) {
5797                    if (MORE_DEBUG) {
5798                        dumpFileMetadata(info);
5799                    }
5800
5801                    final String pkg = info.packageName;
5802                    if (!pkg.equals(mAgentPackage)) {
5803                        // okay, change in package; set up our various
5804                        // bookkeeping if we haven't seen it yet
5805                        if (!mPackagePolicies.containsKey(pkg)) {
5806                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5807                        }
5808
5809                        // Clean up the previous agent relationship if necessary,
5810                        // and let the observer know we're considering a new app.
5811                        if (mAgent != null) {
5812                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
5813                            // Now we're really done
5814                            tearDownPipes();
5815                            tearDownAgent(mTargetApp);
5816                            mTargetApp = null;
5817                            mAgentPackage = null;
5818                        }
5819                    }
5820
5821                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
5822                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
5823                        mPackageInstallers.put(pkg, info.installerPackageName);
5824                        // We've read only the manifest content itself at this point,
5825                        // so consume the footer before looping around to the next
5826                        // input file
5827                        skipTarPadding(info.size, instream);
5828                        sendOnRestorePackage(pkg);
5829                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
5830                        // Metadata blobs!
5831                        readMetadata(info, instream);
5832                        skipTarPadding(info.size, instream);
5833                    } else {
5834                        // Non-manifest, so it's actual file data.  Is this a package
5835                        // we're ignoring?
5836                        boolean okay = true;
5837                        RestorePolicy policy = mPackagePolicies.get(pkg);
5838                        switch (policy) {
5839                            case IGNORE:
5840                                okay = false;
5841                                break;
5842
5843                            case ACCEPT_IF_APK:
5844                                // If we're in accept-if-apk state, then the first file we
5845                                // see MUST be the apk.
5846                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5847                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
5848                                    // Try to install the app.
5849                                    String installerName = mPackageInstallers.get(pkg);
5850                                    okay = installApk(info, installerName, instream);
5851                                    // good to go; promote to ACCEPT
5852                                    mPackagePolicies.put(pkg, (okay)
5853                                            ? RestorePolicy.ACCEPT
5854                                            : RestorePolicy.IGNORE);
5855                                    // At this point we've consumed this file entry
5856                                    // ourselves, so just strip the tar footer and
5857                                    // go on to the next file in the input stream
5858                                    skipTarPadding(info.size, instream);
5859                                    return true;
5860                                } else {
5861                                    // File data before (or without) the apk.  We can't
5862                                    // handle it coherently in this case so ignore it.
5863                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5864                                    okay = false;
5865                                }
5866                                break;
5867
5868                            case ACCEPT:
5869                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
5870                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
5871                                    // we can take the data without the apk, so we
5872                                    // *want* to do so.  skip the apk by declaring this
5873                                    // one file not-okay without changing the restore
5874                                    // policy for the package.
5875                                    okay = false;
5876                                }
5877                                break;
5878
5879                            default:
5880                                // Something has gone dreadfully wrong when determining
5881                                // the restore policy from the manifest.  Ignore the
5882                                // rest of this package's data.
5883                                Slog.e(TAG, "Invalid policy from manifest");
5884                                okay = false;
5885                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5886                                break;
5887                        }
5888
5889                        // The path needs to be canonical
5890                        if (info.path.contains("..") || info.path.contains("//")) {
5891                            if (MORE_DEBUG) {
5892                                Slog.w(TAG, "Dropping invalid path " + info.path);
5893                            }
5894                            okay = false;
5895                        }
5896
5897                        // If the policy is satisfied, go ahead and set up to pipe the
5898                        // data to the agent.
5899                        if (DEBUG && okay && mAgent != null) {
5900                            Slog.i(TAG, "Reusing existing agent instance");
5901                        }
5902                        if (okay && mAgent == null) {
5903                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
5904
5905                            try {
5906                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
5907
5908                                // If we haven't sent any data to this app yet, we probably
5909                                // need to clear it first.  Check that.
5910                                if (!mClearedPackages.contains(pkg)) {
5911                                    // apps with their own backup agents are
5912                                    // responsible for coherently managing a full
5913                                    // restore.
5914                                    if (mTargetApp.backupAgentName == null) {
5915                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
5916                                        clearApplicationDataSynchronous(pkg);
5917                                    } else {
5918                                        if (DEBUG) Slog.d(TAG, "backup agent ("
5919                                                + mTargetApp.backupAgentName + ") => no clear");
5920                                    }
5921                                    mClearedPackages.add(pkg);
5922                                } else {
5923                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
5924                                }
5925
5926                                // All set; now set up the IPC and launch the agent
5927                                setUpPipes();
5928                                mAgent = bindToAgentSynchronous(mTargetApp,
5929                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
5930                                mAgentPackage = pkg;
5931                            } catch (IOException e) {
5932                                // fall through to error handling
5933                            } catch (NameNotFoundException e) {
5934                                // fall through to error handling
5935                            }
5936
5937                            if (mAgent == null) {
5938                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
5939                                okay = false;
5940                                tearDownPipes();
5941                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5942                            }
5943                        }
5944
5945                        // Sanity check: make sure we never give data to the wrong app.  This
5946                        // should never happen but a little paranoia here won't go amiss.
5947                        if (okay && !pkg.equals(mAgentPackage)) {
5948                            Slog.e(TAG, "Restoring data for " + pkg
5949                                    + " but agent is for " + mAgentPackage);
5950                            okay = false;
5951                        }
5952
5953                        // At this point we have an agent ready to handle the full
5954                        // restore data as well as a pipe for sending data to
5955                        // that agent.  Tell the agent to start reading from the
5956                        // pipe.
5957                        if (okay) {
5958                            boolean agentSuccess = true;
5959                            long toCopy = info.size;
5960                            final int token = generateToken();
5961                            try {
5962                                prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
5963                                if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
5964                                    if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
5965                                            + " : " + info.path);
5966                                    mObbConnection.restoreObbFile(pkg, mPipes[0],
5967                                            info.size, info.type, info.path, info.mode,
5968                                            info.mtime, token, mBackupManagerBinder);
5969                                } else {
5970                                    if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
5971                                            + info.path);
5972                                    // fire up the app's agent listening on the socket.  If
5973                                    // the agent is running in the system process we can't
5974                                    // just invoke it asynchronously, so we provide a thread
5975                                    // for it here.
5976                                    if (mTargetApp.processName.equals("system")) {
5977                                        Slog.d(TAG, "system process agent - spinning a thread");
5978                                        RestoreFileRunnable runner = new RestoreFileRunnable(
5979                                                mAgent, info, mPipes[0], token);
5980                                        new Thread(runner, "restore-sys-runner").start();
5981                                    } else {
5982                                        mAgent.doRestoreFile(mPipes[0], info.size, info.type,
5983                                                info.domain, info.path, info.mode, info.mtime,
5984                                                token, mBackupManagerBinder);
5985                                    }
5986                                }
5987                            } catch (IOException e) {
5988                                // couldn't dup the socket for a process-local restore
5989                                Slog.d(TAG, "Couldn't establish restore");
5990                                agentSuccess = false;
5991                                okay = false;
5992                            } catch (RemoteException e) {
5993                                // whoops, remote entity went away.  We'll eat the content
5994                                // ourselves, then, and not copy it over.
5995                                Slog.e(TAG, "Agent crashed during full restore");
5996                                agentSuccess = false;
5997                                okay = false;
5998                            }
5999
6000                            // Copy over the data if the agent is still good
6001                            if (okay) {
6002                                boolean pipeOkay = true;
6003                                FileOutputStream pipe = new FileOutputStream(
6004                                        mPipes[1].getFileDescriptor());
6005                                while (toCopy > 0) {
6006                                    int toRead = (toCopy > buffer.length)
6007                                    ? buffer.length : (int)toCopy;
6008                                    int nRead = instream.read(buffer, 0, toRead);
6009                                    if (nRead >= 0) mBytes += nRead;
6010                                    if (nRead <= 0) break;
6011                                    toCopy -= nRead;
6012
6013                                    // send it to the output pipe as long as things
6014                                    // are still good
6015                                    if (pipeOkay) {
6016                                        try {
6017                                            pipe.write(buffer, 0, nRead);
6018                                        } catch (IOException e) {
6019                                            Slog.e(TAG, "Failed to write to restore pipe", e);
6020                                            pipeOkay = false;
6021                                        }
6022                                    }
6023                                }
6024
6025                                // done sending that file!  Now we just need to consume
6026                                // the delta from info.size to the end of block.
6027                                skipTarPadding(info.size, instream);
6028
6029                                // and now that we've sent it all, wait for the remote
6030                                // side to acknowledge receipt
6031                                agentSuccess = waitUntilOperationComplete(token);
6032                            }
6033
6034                            // okay, if the remote end failed at any point, deal with
6035                            // it by ignoring the rest of the restore on it
6036                            if (!agentSuccess) {
6037                                mBackupHandler.removeMessages(MSG_TIMEOUT);
6038                                tearDownPipes();
6039                                tearDownAgent(mTargetApp);
6040                                mAgent = null;
6041                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6042                            }
6043                        }
6044
6045                        // Problems setting up the agent communication, or an already-
6046                        // ignored package: skip to the next tar stream entry by
6047                        // reading and discarding this file.
6048                        if (!okay) {
6049                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
6050                            long bytesToConsume = (info.size + 511) & ~511;
6051                            while (bytesToConsume > 0) {
6052                                int toRead = (bytesToConsume > buffer.length)
6053                                ? buffer.length : (int)bytesToConsume;
6054                                long nRead = instream.read(buffer, 0, toRead);
6055                                if (nRead >= 0) mBytes += nRead;
6056                                if (nRead <= 0) break;
6057                                bytesToConsume -= nRead;
6058                            }
6059                        }
6060                    }
6061                }
6062            } catch (IOException e) {
6063                if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
6064                // treat as EOF
6065                info = null;
6066            }
6067
6068            return (info != null);
6069        }
6070
6071        void setUpPipes() throws IOException {
6072            mPipes = ParcelFileDescriptor.createPipe();
6073        }
6074
6075        void tearDownPipes() {
6076            if (mPipes != null) {
6077                try {
6078                    mPipes[0].close();
6079                    mPipes[0] = null;
6080                    mPipes[1].close();
6081                    mPipes[1] = null;
6082                } catch (IOException e) {
6083                    Slog.w(TAG, "Couldn't close agent pipes", e);
6084                }
6085                mPipes = null;
6086            }
6087        }
6088
6089        void tearDownAgent(ApplicationInfo app) {
6090            if (mAgent != null) {
6091                try {
6092                    // unbind and tidy up even on timeout or failure, just in case
6093                    mActivityManager.unbindBackupAgent(app);
6094
6095                    // The agent was running with a stub Application object, so shut it down.
6096                    // !!! We hardcode the confirmation UI's package name here rather than use a
6097                    //     manifest flag!  TODO something less direct.
6098                    if (app.uid != Process.SYSTEM_UID
6099                            && !app.packageName.equals("com.android.backupconfirm")) {
6100                        if (DEBUG) Slog.d(TAG, "Killing host process");
6101                        mActivityManager.killApplicationProcess(app.processName, app.uid);
6102                    } else {
6103                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
6104                    }
6105                } catch (RemoteException e) {
6106                    Slog.d(TAG, "Lost app trying to shut down");
6107                }
6108                mAgent = null;
6109            }
6110        }
6111
6112        class RestoreInstallObserver extends IPackageInstallObserver.Stub {
6113            final AtomicBoolean mDone = new AtomicBoolean();
6114            String mPackageName;
6115            int mResult;
6116
6117            public void reset() {
6118                synchronized (mDone) {
6119                    mDone.set(false);
6120                }
6121            }
6122
6123            public void waitForCompletion() {
6124                synchronized (mDone) {
6125                    while (mDone.get() == false) {
6126                        try {
6127                            mDone.wait();
6128                        } catch (InterruptedException e) { }
6129                    }
6130                }
6131            }
6132
6133            int getResult() {
6134                return mResult;
6135            }
6136
6137            @Override
6138            public void packageInstalled(String packageName, int returnCode)
6139                    throws RemoteException {
6140                synchronized (mDone) {
6141                    mResult = returnCode;
6142                    mPackageName = packageName;
6143                    mDone.set(true);
6144                    mDone.notifyAll();
6145                }
6146            }
6147        }
6148
6149        class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
6150            final AtomicBoolean mDone = new AtomicBoolean();
6151            int mResult;
6152
6153            public void reset() {
6154                synchronized (mDone) {
6155                    mDone.set(false);
6156                }
6157            }
6158
6159            public void waitForCompletion() {
6160                synchronized (mDone) {
6161                    while (mDone.get() == false) {
6162                        try {
6163                            mDone.wait();
6164                        } catch (InterruptedException e) { }
6165                    }
6166                }
6167            }
6168
6169            @Override
6170            public void packageDeleted(String packageName, int returnCode) throws RemoteException {
6171                synchronized (mDone) {
6172                    mResult = returnCode;
6173                    mDone.set(true);
6174                    mDone.notifyAll();
6175                }
6176            }
6177        }
6178
6179        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
6180        final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
6181
6182        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
6183            boolean okay = true;
6184
6185            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
6186
6187            // The file content is an .apk file.  Copy it out to a staging location and
6188            // attempt to install it.
6189            File apkFile = new File(mDataDir, info.packageName);
6190            try {
6191                FileOutputStream apkStream = new FileOutputStream(apkFile);
6192                byte[] buffer = new byte[32 * 1024];
6193                long size = info.size;
6194                while (size > 0) {
6195                    long toRead = (buffer.length < size) ? buffer.length : size;
6196                    int didRead = instream.read(buffer, 0, (int)toRead);
6197                    if (didRead >= 0) mBytes += didRead;
6198                    apkStream.write(buffer, 0, didRead);
6199                    size -= didRead;
6200                }
6201                apkStream.close();
6202
6203                // make sure the installer can read it
6204                apkFile.setReadable(true, false);
6205
6206                // Now install it
6207                Uri packageUri = Uri.fromFile(apkFile);
6208                mInstallObserver.reset();
6209                mPackageManager.installPackage(packageUri, mInstallObserver,
6210                        PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
6211                        installerPackage);
6212                mInstallObserver.waitForCompletion();
6213
6214                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
6215                    // The only time we continue to accept install of data even if the
6216                    // apk install failed is if we had already determined that we could
6217                    // accept the data regardless.
6218                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
6219                        okay = false;
6220                    }
6221                } else {
6222                    // Okay, the install succeeded.  Make sure it was the right app.
6223                    boolean uninstall = false;
6224                    if (!mInstallObserver.mPackageName.equals(info.packageName)) {
6225                        Slog.w(TAG, "Restore stream claimed to include apk for "
6226                                + info.packageName + " but apk was really "
6227                                + mInstallObserver.mPackageName);
6228                        // delete the package we just put in place; it might be fraudulent
6229                        okay = false;
6230                        uninstall = true;
6231                    } else {
6232                        try {
6233                            PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
6234                                    PackageManager.GET_SIGNATURES);
6235                            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
6236                                Slog.w(TAG, "Restore stream contains apk of package "
6237                                        + info.packageName + " but it disallows backup/restore");
6238                                okay = false;
6239                            } else {
6240                                // So far so good -- do the signatures match the manifest?
6241                                Signature[] sigs = mManifestSignatures.get(info.packageName);
6242                                if (signaturesMatch(sigs, pkg)) {
6243                                    // If this is a system-uid app without a declared backup agent,
6244                                    // don't restore any of the file data.
6245                                    if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
6246                                            && (pkg.applicationInfo.backupAgentName == null)) {
6247                                        Slog.w(TAG, "Installed app " + info.packageName
6248                                                + " has restricted uid and no agent");
6249                                        okay = false;
6250                                    }
6251                                } else {
6252                                    Slog.w(TAG, "Installed app " + info.packageName
6253                                            + " signatures do not match restore manifest");
6254                                    okay = false;
6255                                    uninstall = true;
6256                                }
6257                            }
6258                        } catch (NameNotFoundException e) {
6259                            Slog.w(TAG, "Install of package " + info.packageName
6260                                    + " succeeded but now not found");
6261                            okay = false;
6262                        }
6263                    }
6264
6265                    // If we're not okay at this point, we need to delete the package
6266                    // that we just installed.
6267                    if (uninstall) {
6268                        mDeleteObserver.reset();
6269                        mPackageManager.deletePackage(mInstallObserver.mPackageName,
6270                                mDeleteObserver, 0);
6271                        mDeleteObserver.waitForCompletion();
6272                    }
6273                }
6274            } catch (IOException e) {
6275                Slog.e(TAG, "Unable to transcribe restored apk for install");
6276                okay = false;
6277            } finally {
6278                apkFile.delete();
6279            }
6280
6281            return okay;
6282        }
6283
6284        // Given an actual file content size, consume the post-content padding mandated
6285        // by the tar format.
6286        void skipTarPadding(long size, InputStream instream) throws IOException {
6287            long partial = (size + 512) % 512;
6288            if (partial > 0) {
6289                final int needed = 512 - (int)partial;
6290                byte[] buffer = new byte[needed];
6291                if (readExactly(instream, buffer, 0, needed) == needed) {
6292                    mBytes += needed;
6293                } else throw new IOException("Unexpected EOF in padding");
6294            }
6295        }
6296
6297        // Read a widget metadata file, returning the restored blob
6298        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
6299            // Fail on suspiciously large widget dump files
6300            if (info.size > 64 * 1024) {
6301                throw new IOException("Metadata too big; corrupt? size=" + info.size);
6302            }
6303
6304            byte[] buffer = new byte[(int) info.size];
6305            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6306                mBytes += info.size;
6307            } else throw new IOException("Unexpected EOF in widget data");
6308
6309            String[] str = new String[1];
6310            int offset = extractLine(buffer, 0, str);
6311            int version = Integer.parseInt(str[0]);
6312            if (version == BACKUP_MANIFEST_VERSION) {
6313                offset = extractLine(buffer, offset, str);
6314                final String pkg = str[0];
6315                if (info.packageName.equals(pkg)) {
6316                    // Data checks out -- the rest of the buffer is a concatenation of
6317                    // binary blobs as described in the comment at writeAppWidgetData()
6318                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
6319                            offset, buffer.length - offset);
6320                    DataInputStream in = new DataInputStream(bin);
6321                    while (bin.available() > 0) {
6322                        int token = in.readInt();
6323                        int size = in.readInt();
6324                        if (size > 64 * 1024) {
6325                            throw new IOException("Datum "
6326                                    + Integer.toHexString(token)
6327                                    + " too big; corrupt? size=" + info.size);
6328                        }
6329                        switch (token) {
6330                            case BACKUP_WIDGET_METADATA_TOKEN:
6331                            {
6332                                if (MORE_DEBUG) {
6333                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
6334                                }
6335                                mWidgetData = new byte[size];
6336                                in.read(mWidgetData);
6337                                break;
6338                            }
6339                            default:
6340                            {
6341                                if (DEBUG) {
6342                                    Slog.i(TAG, "Ignoring metadata blob "
6343                                            + Integer.toHexString(token)
6344                                            + " for " + info.packageName);
6345                                }
6346                                in.skipBytes(size);
6347                                break;
6348                            }
6349                        }
6350                    }
6351                } else {
6352                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
6353                            + " but widget data for " + pkg);
6354                }
6355            } else {
6356                Slog.w(TAG, "Unsupported metadata version " + version);
6357            }
6358        }
6359
6360        // Returns a policy constant; takes a buffer arg to reduce memory churn
6361        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
6362                throws IOException {
6363            // Fail on suspiciously large manifest files
6364            if (info.size > 64 * 1024) {
6365                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
6366            }
6367
6368            byte[] buffer = new byte[(int) info.size];
6369            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6370                mBytes += info.size;
6371            } else throw new IOException("Unexpected EOF in manifest");
6372
6373            RestorePolicy policy = RestorePolicy.IGNORE;
6374            String[] str = new String[1];
6375            int offset = 0;
6376
6377            try {
6378                offset = extractLine(buffer, offset, str);
6379                int version = Integer.parseInt(str[0]);
6380                if (version == BACKUP_MANIFEST_VERSION) {
6381                    offset = extractLine(buffer, offset, str);
6382                    String manifestPackage = str[0];
6383                    // TODO: handle <original-package>
6384                    if (manifestPackage.equals(info.packageName)) {
6385                        offset = extractLine(buffer, offset, str);
6386                        version = Integer.parseInt(str[0]);  // app version
6387                        offset = extractLine(buffer, offset, str);
6388                        int platformVersion = Integer.parseInt(str[0]);
6389                        offset = extractLine(buffer, offset, str);
6390                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
6391                        offset = extractLine(buffer, offset, str);
6392                        boolean hasApk = str[0].equals("1");
6393                        offset = extractLine(buffer, offset, str);
6394                        int numSigs = Integer.parseInt(str[0]);
6395                        if (numSigs > 0) {
6396                            Signature[] sigs = new Signature[numSigs];
6397                            for (int i = 0; i < numSigs; i++) {
6398                                offset = extractLine(buffer, offset, str);
6399                                sigs[i] = new Signature(str[0]);
6400                            }
6401                            mManifestSignatures.put(info.packageName, sigs);
6402
6403                            // Okay, got the manifest info we need...
6404                            try {
6405                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
6406                                        info.packageName, PackageManager.GET_SIGNATURES);
6407                                // Fall through to IGNORE if the app explicitly disallows backup
6408                                final int flags = pkgInfo.applicationInfo.flags;
6409                                if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
6410                                    // Restore system-uid-space packages only if they have
6411                                    // defined a custom backup agent
6412                                    if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
6413                                            || (pkgInfo.applicationInfo.backupAgentName != null)) {
6414                                        // Verify signatures against any installed version; if they
6415                                        // don't match, then we fall though and ignore the data.  The
6416                                        // signatureMatch() method explicitly ignores the signature
6417                                        // check for packages installed on the system partition, because
6418                                        // such packages are signed with the platform cert instead of
6419                                        // the app developer's cert, so they're different on every
6420                                        // device.
6421                                        if (signaturesMatch(sigs, pkgInfo)) {
6422                                            if (pkgInfo.versionCode >= version) {
6423                                                Slog.i(TAG, "Sig + version match; taking data");
6424                                                policy = RestorePolicy.ACCEPT;
6425                                            } else {
6426                                                // The data is from a newer version of the app than
6427                                                // is presently installed.  That means we can only
6428                                                // use it if the matching apk is also supplied.
6429                                                Slog.d(TAG, "Data version " + version
6430                                                        + " is newer than installed version "
6431                                                        + pkgInfo.versionCode + " - requiring apk");
6432                                                policy = RestorePolicy.ACCEPT_IF_APK;
6433                                            }
6434                                        } else {
6435                                            Slog.w(TAG, "Restore manifest signatures do not match "
6436                                                    + "installed application for " + info.packageName);
6437                                        }
6438                                    } else {
6439                                        Slog.w(TAG, "Package " + info.packageName
6440                                                + " is system level with no agent");
6441                                    }
6442                                } else {
6443                                    if (DEBUG) Slog.i(TAG, "Restore manifest from "
6444                                            + info.packageName + " but allowBackup=false");
6445                                }
6446                            } catch (NameNotFoundException e) {
6447                                // Okay, the target app isn't installed.  We can process
6448                                // the restore properly only if the dataset provides the
6449                                // apk file and we can successfully install it.
6450                                if (DEBUG) Slog.i(TAG, "Package " + info.packageName
6451                                        + " not installed; requiring apk in dataset");
6452                                policy = RestorePolicy.ACCEPT_IF_APK;
6453                            }
6454
6455                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
6456                                Slog.i(TAG, "Cannot restore package " + info.packageName
6457                                        + " without the matching .apk");
6458                            }
6459                        } else {
6460                            Slog.i(TAG, "Missing signature on backed-up package "
6461                                    + info.packageName);
6462                        }
6463                    } else {
6464                        Slog.i(TAG, "Expected package " + info.packageName
6465                                + " but restore manifest claims " + manifestPackage);
6466                    }
6467                } else {
6468                    Slog.i(TAG, "Unknown restore manifest version " + version
6469                            + " for package " + info.packageName);
6470                }
6471            } catch (NumberFormatException e) {
6472                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
6473            } catch (IllegalArgumentException e) {
6474                Slog.w(TAG, e.getMessage());
6475            }
6476
6477            return policy;
6478        }
6479
6480        // Builds a line from a byte buffer starting at 'offset', and returns
6481        // the index of the next unconsumed data in the buffer.
6482        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
6483            final int end = buffer.length;
6484            if (offset >= end) throw new IOException("Incomplete data");
6485
6486            int pos;
6487            for (pos = offset; pos < end; pos++) {
6488                byte c = buffer[pos];
6489                // at LF we declare end of line, and return the next char as the
6490                // starting point for the next time through
6491                if (c == '\n') {
6492                    break;
6493                }
6494            }
6495            outStr[0] = new String(buffer, offset, pos - offset);
6496            pos++;  // may be pointing an extra byte past the end but that's okay
6497            return pos;
6498        }
6499
6500        void dumpFileMetadata(FileMetadata info) {
6501            if (DEBUG) {
6502                StringBuilder b = new StringBuilder(128);
6503
6504                // mode string
6505                b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
6506                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
6507                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
6508                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
6509                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
6510                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
6511                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
6512                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
6513                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
6514                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
6515                b.append(String.format(" %9d ", info.size));
6516
6517                Date stamp = new Date(info.mtime);
6518                b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
6519
6520                b.append(info.packageName);
6521                b.append(" :: ");
6522                b.append(info.domain);
6523                b.append(" :: ");
6524                b.append(info.path);
6525
6526                Slog.i(TAG, b.toString());
6527            }
6528        }
6529        // Consume a tar file header block [sequence] and accumulate the relevant metadata
6530        FileMetadata readTarHeaders(InputStream instream) throws IOException {
6531            byte[] block = new byte[512];
6532            FileMetadata info = null;
6533
6534            boolean gotHeader = readTarHeader(instream, block);
6535            if (gotHeader) {
6536                try {
6537                    // okay, presume we're okay, and extract the various metadata
6538                    info = new FileMetadata();
6539                    info.size = extractRadix(block, 124, 12, 8);
6540                    info.mtime = extractRadix(block, 136, 12, 8);
6541                    info.mode = extractRadix(block, 100, 8, 8);
6542
6543                    info.path = extractString(block, 345, 155); // prefix
6544                    String path = extractString(block, 0, 100);
6545                    if (path.length() > 0) {
6546                        if (info.path.length() > 0) info.path += '/';
6547                        info.path += path;
6548                    }
6549
6550                    // tar link indicator field: 1 byte at offset 156 in the header.
6551                    int typeChar = block[156];
6552                    if (typeChar == 'x') {
6553                        // pax extended header, so we need to read that
6554                        gotHeader = readPaxExtendedHeader(instream, info);
6555                        if (gotHeader) {
6556                            // and after a pax extended header comes another real header -- read
6557                            // that to find the real file type
6558                            gotHeader = readTarHeader(instream, block);
6559                        }
6560                        if (!gotHeader) throw new IOException("Bad or missing pax header");
6561
6562                        typeChar = block[156];
6563                    }
6564
6565                    switch (typeChar) {
6566                        case '0': info.type = BackupAgent.TYPE_FILE; break;
6567                        case '5': {
6568                            info.type = BackupAgent.TYPE_DIRECTORY;
6569                            if (info.size != 0) {
6570                                Slog.w(TAG, "Directory entry with nonzero size in header");
6571                                info.size = 0;
6572                            }
6573                            break;
6574                        }
6575                        case 0: {
6576                            // presume EOF
6577                            if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
6578                            return null;
6579                        }
6580                        default: {
6581                            Slog.e(TAG, "Unknown tar entity type: " + typeChar);
6582                            throw new IOException("Unknown entity type " + typeChar);
6583                        }
6584                    }
6585
6586                    // Parse out the path
6587                    //
6588                    // first: apps/shared/unrecognized
6589                    if (FullBackup.SHARED_PREFIX.regionMatches(0,
6590                            info.path, 0, FullBackup.SHARED_PREFIX.length())) {
6591                        // File in shared storage.  !!! TODO: implement this.
6592                        info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
6593                        info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
6594                        info.domain = FullBackup.SHARED_STORAGE_TOKEN;
6595                        if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
6596                    } else if (FullBackup.APPS_PREFIX.regionMatches(0,
6597                            info.path, 0, FullBackup.APPS_PREFIX.length())) {
6598                        // App content!  Parse out the package name and domain
6599
6600                        // strip the apps/ prefix
6601                        info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
6602
6603                        // extract the package name
6604                        int slash = info.path.indexOf('/');
6605                        if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
6606                        info.packageName = info.path.substring(0, slash);
6607                        info.path = info.path.substring(slash+1);
6608
6609                        // if it's a manifest or metadata payload we're done, otherwise parse
6610                        // out the domain into which the file will be restored
6611                        if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
6612                                && !info.path.equals(BACKUP_METADATA_FILENAME)) {
6613                            slash = info.path.indexOf('/');
6614                            if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
6615                            info.domain = info.path.substring(0, slash);
6616                            info.path = info.path.substring(slash + 1);
6617                        }
6618                    }
6619                } catch (IOException e) {
6620                    if (DEBUG) {
6621                        Slog.e(TAG, "Parse error in header: " + e.getMessage());
6622                        HEXLOG(block);
6623                    }
6624                    throw e;
6625                }
6626            }
6627            return info;
6628        }
6629
6630        private void HEXLOG(byte[] block) {
6631            int offset = 0;
6632            int todo = block.length;
6633            StringBuilder buf = new StringBuilder(64);
6634            while (todo > 0) {
6635                buf.append(String.format("%04x   ", offset));
6636                int numThisLine = (todo > 16) ? 16 : todo;
6637                for (int i = 0; i < numThisLine; i++) {
6638                    buf.append(String.format("%02x ", block[offset+i]));
6639                }
6640                Slog.i("hexdump", buf.toString());
6641                buf.setLength(0);
6642                todo -= numThisLine;
6643                offset += numThisLine;
6644            }
6645        }
6646
6647        // Read exactly the given number of bytes into a buffer at the stated offset.
6648        // Returns false if EOF is encountered before the requested number of bytes
6649        // could be read.
6650        int readExactly(InputStream in, byte[] buffer, int offset, int size)
6651                throws IOException {
6652            if (size <= 0) throw new IllegalArgumentException("size must be > 0");
6653
6654            int soFar = 0;
6655            while (soFar < size) {
6656                int nRead = in.read(buffer, offset + soFar, size - soFar);
6657                if (nRead <= 0) {
6658                    if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
6659                    break;
6660                }
6661                soFar += nRead;
6662            }
6663            return soFar;
6664        }
6665
6666        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
6667            final int got = readExactly(instream, block, 0, 512);
6668            if (got == 0) return false;     // Clean EOF
6669            if (got < 512) throw new IOException("Unable to read full block header");
6670            mBytes += 512;
6671            return true;
6672        }
6673
6674        // overwrites 'info' fields based on the pax extended header
6675        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
6676                throws IOException {
6677            // We should never see a pax extended header larger than this
6678            if (info.size > 32*1024) {
6679                Slog.w(TAG, "Suspiciously large pax header size " + info.size
6680                        + " - aborting");
6681                throw new IOException("Sanity failure: pax header size " + info.size);
6682            }
6683
6684            // read whole blocks, not just the content size
6685            int numBlocks = (int)((info.size + 511) >> 9);
6686            byte[] data = new byte[numBlocks * 512];
6687            if (readExactly(instream, data, 0, data.length) < data.length) {
6688                throw new IOException("Unable to read full pax header");
6689            }
6690            mBytes += data.length;
6691
6692            final int contentSize = (int) info.size;
6693            int offset = 0;
6694            do {
6695                // extract the line at 'offset'
6696                int eol = offset+1;
6697                while (eol < contentSize && data[eol] != ' ') eol++;
6698                if (eol >= contentSize) {
6699                    // error: we just hit EOD looking for the end of the size field
6700                    throw new IOException("Invalid pax data");
6701                }
6702                // eol points to the space between the count and the key
6703                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
6704                int key = eol + 1;  // start of key=value
6705                eol = offset + linelen - 1; // trailing LF
6706                int value;
6707                for (value = key+1; data[value] != '=' && value <= eol; value++);
6708                if (value > eol) {
6709                    throw new IOException("Invalid pax declaration");
6710                }
6711
6712                // pax requires that key/value strings be in UTF-8
6713                String keyStr = new String(data, key, value-key, "UTF-8");
6714                // -1 to strip the trailing LF
6715                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
6716
6717                if ("path".equals(keyStr)) {
6718                    info.path = valStr;
6719                } else if ("size".equals(keyStr)) {
6720                    info.size = Long.parseLong(valStr);
6721                } else {
6722                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
6723                }
6724
6725                offset += linelen;
6726            } while (offset < contentSize);
6727
6728            return true;
6729        }
6730
6731        long extractRadix(byte[] data, int offset, int maxChars, int radix)
6732                throws IOException {
6733            long value = 0;
6734            final int end = offset + maxChars;
6735            for (int i = offset; i < end; i++) {
6736                final byte b = data[i];
6737                // Numeric fields in tar can terminate with either NUL or SPC
6738                if (b == 0 || b == ' ') break;
6739                if (b < '0' || b > ('0' + radix - 1)) {
6740                    throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix);
6741                }
6742                value = radix * value + (b - '0');
6743            }
6744            return value;
6745        }
6746
6747        String extractString(byte[] data, int offset, int maxChars) throws IOException {
6748            final int end = offset + maxChars;
6749            int eos = offset;
6750            // tar string fields terminate early with a NUL
6751            while (eos < end && data[eos] != 0) eos++;
6752            return new String(data, offset, eos-offset, "US-ASCII");
6753        }
6754
6755        void sendStartRestore() {
6756            if (mObserver != null) {
6757                try {
6758                    mObserver.onStartRestore();
6759                } catch (RemoteException e) {
6760                    Slog.w(TAG, "full restore observer went away: startRestore");
6761                    mObserver = null;
6762                }
6763            }
6764        }
6765
6766        void sendOnRestorePackage(String name) {
6767            if (mObserver != null) {
6768                try {
6769                    // TODO: use a more user-friendly name string
6770                    mObserver.onRestorePackage(name);
6771                } catch (RemoteException e) {
6772                    Slog.w(TAG, "full restore observer went away: restorePackage");
6773                    mObserver = null;
6774                }
6775            }
6776        }
6777
6778        void sendEndRestore() {
6779            if (mObserver != null) {
6780                try {
6781                    mObserver.onEndRestore();
6782                } catch (RemoteException e) {
6783                    Slog.w(TAG, "full restore observer went away: endRestore");
6784                    mObserver = null;
6785                }
6786            }
6787        }
6788    }
6789
6790    // ----- Restore handling -----
6791
6792    // new style: we only store the SHA-1 hashes of each sig, not the full block
6793    static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
6794        if (target == null) {
6795            return false;
6796        }
6797
6798        // If the target resides on the system partition, we allow it to restore
6799        // data from the like-named package in a restore set even if the signatures
6800        // do not match.  (Unlike general applications, those flashed to the system
6801        // partition will be signed with the device's platform certificate, so on
6802        // different phones the same system app will have different signatures.)
6803        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
6804            if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
6805            return true;
6806        }
6807
6808        // Allow unsigned apps, but not signed on one device and unsigned on the other
6809        // !!! TODO: is this the right policy?
6810        Signature[] deviceSigs = target.signatures;
6811        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
6812                + " device=" + deviceSigs);
6813        if ((storedSigHashes == null || storedSigHashes.size() == 0)
6814                && (deviceSigs == null || deviceSigs.length == 0)) {
6815            return true;
6816        }
6817        if (storedSigHashes == null || deviceSigs == null) {
6818            return false;
6819        }
6820
6821        // !!! TODO: this demands that every stored signature match one
6822        // that is present on device, and does not demand the converse.
6823        // Is this this right policy?
6824        final int nStored = storedSigHashes.size();
6825        final int nDevice = deviceSigs.length;
6826
6827        // hash each on-device signature
6828        ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
6829        for (int i = 0; i < nDevice; i++) {
6830            deviceHashes.add(hashSignature(deviceSigs[i]));
6831        }
6832
6833        // now ensure that each stored sig (hash) matches an on-device sig (hash)
6834        for (int n = 0; n < nStored; n++) {
6835            boolean match = false;
6836            final byte[] storedHash = storedSigHashes.get(n);
6837            for (int i = 0; i < nDevice; i++) {
6838                if (Arrays.equals(storedHash, deviceHashes.get(i))) {
6839                    match = true;
6840                    break;
6841                }
6842            }
6843            // match is false when no on-device sig matched one of the stored ones
6844            if (!match) {
6845                return false;
6846            }
6847        }
6848
6849        return true;
6850    }
6851
6852    static byte[] hashSignature(Signature sig) {
6853        try {
6854            MessageDigest digest = MessageDigest.getInstance("SHA-256");
6855            digest.update(sig.toByteArray());
6856            return digest.digest();
6857        } catch (NoSuchAlgorithmException e) {
6858            Slog.w(TAG, "No SHA-256 algorithm found!");
6859        }
6860        return null;
6861    }
6862
6863    // Old style: directly match the stored vs on device signature blocks
6864    static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
6865        if (target == null) {
6866            return false;
6867        }
6868
6869        // If the target resides on the system partition, we allow it to restore
6870        // data from the like-named package in a restore set even if the signatures
6871        // do not match.  (Unlike general applications, those flashed to the system
6872        // partition will be signed with the device's platform certificate, so on
6873        // different phones the same system app will have different signatures.)
6874        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
6875            if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
6876            return true;
6877        }
6878
6879        // Allow unsigned apps, but not signed on one device and unsigned on the other
6880        // !!! TODO: is this the right policy?
6881        Signature[] deviceSigs = target.signatures;
6882        if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
6883                + " device=" + deviceSigs);
6884        if ((storedSigs == null || storedSigs.length == 0)
6885                && (deviceSigs == null || deviceSigs.length == 0)) {
6886            return true;
6887        }
6888        if (storedSigs == null || deviceSigs == null) {
6889            return false;
6890        }
6891
6892        // !!! TODO: this demands that every stored signature match one
6893        // that is present on device, and does not demand the converse.
6894        // Is this this right policy?
6895        int nStored = storedSigs.length;
6896        int nDevice = deviceSigs.length;
6897
6898        for (int i=0; i < nStored; i++) {
6899            boolean match = false;
6900            for (int j=0; j < nDevice; j++) {
6901                if (storedSigs[i].equals(deviceSigs[j])) {
6902                    match = true;
6903                    break;
6904                }
6905            }
6906            if (!match) {
6907                return false;
6908            }
6909        }
6910        return true;
6911    }
6912
6913    // Used by both incremental and full restore
6914    void restoreWidgetData(String packageName, byte[] widgetData) {
6915        // Apply the restored widget state and generate the ID update for the app
6916        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER);
6917    }
6918
6919    // *****************************
6920    // NEW UNIFIED RESTORE IMPLEMENTATION
6921    // *****************************
6922
6923    // states of the unified-restore state machine
6924    enum UnifiedRestoreState {
6925        INITIAL,
6926        RUNNING_QUEUE,
6927        RESTORE_KEYVALUE,
6928        RESTORE_FULL,
6929        RESTORE_FINISHED,
6930        FINAL
6931    }
6932
6933    class PerformUnifiedRestoreTask implements BackupRestoreTask {
6934        // Transport we're working with to do the restore
6935        private IBackupTransport mTransport;
6936
6937        // Where per-transport saved state goes
6938        File mStateDir;
6939
6940        // Restore observer; may be null
6941        private IRestoreObserver mObserver;
6942
6943        // Token identifying the dataset to the transport
6944        private long mToken;
6945
6946        // When this is a restore-during-install, this is the token identifying the
6947        // operation to the Package Manager, and we must ensure that we let it know
6948        // when we're finished.
6949        private int mPmToken;
6950
6951        // Is this a whole-system restore, i.e. are we establishing a new ancestral
6952        // dataset to base future restore-at-install operations from?
6953        private boolean mIsSystemRestore;
6954
6955        // If this is a single-package restore, what package are we interested in?
6956        private PackageInfo mTargetPackage;
6957
6958        // In all cases, the calculated list of packages that we are trying to restore
6959        private List<PackageInfo> mAcceptSet;
6960
6961        // Our bookkeeping about the ancestral dataset
6962        private PackageManagerBackupAgent mPmAgent;
6963
6964        // Currently-bound backup agent for restore + restoreFinished purposes
6965        private IBackupAgent mAgent;
6966
6967        // What sort of restore we're doing now
6968        private RestoreDescription mRestoreDescription;
6969
6970        // The package we're currently restoring
6971        private PackageInfo mCurrentPackage;
6972
6973        // Widget-related data handled as part of this restore operation
6974        private byte[] mWidgetData;
6975
6976        // Number of apps restored in this pass
6977        private int mCount;
6978
6979        // When did we start?
6980        private long mStartRealtime;
6981
6982        // State machine progress
6983        private UnifiedRestoreState mState;
6984
6985        // How are things going?
6986        private int mStatus;
6987
6988        // Done?
6989        private boolean mFinished;
6990
6991        // Key/value: bookkeeping about staged data and files for agent access
6992        private File mBackupDataName;
6993        private File mStageName;
6994        private File mSavedStateName;
6995        private File mNewStateName;
6996        ParcelFileDescriptor mBackupData;
6997        ParcelFileDescriptor mNewState;
6998
6999        // Invariant: mWakelock is already held, and this task is responsible for
7000        // releasing it at the end of the restore operation.
7001        PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer,
7002                long restoreSetToken, PackageInfo targetPackage, int pmToken,
7003                boolean isFullSystemRestore, String[] filterSet) {
7004            mState = UnifiedRestoreState.INITIAL;
7005            mStartRealtime = SystemClock.elapsedRealtime();
7006
7007            mTransport = transport;
7008            mObserver = observer;
7009            mToken = restoreSetToken;
7010            mPmToken = pmToken;
7011            mTargetPackage = targetPackage;
7012            mIsSystemRestore = isFullSystemRestore;
7013            mFinished = false;
7014
7015            if (targetPackage != null) {
7016                // Single package restore
7017                mAcceptSet = new ArrayList<PackageInfo>();
7018                mAcceptSet.add(targetPackage);
7019            } else {
7020                // Everything possible, or a target set
7021                if (filterSet == null) {
7022                    // We want everything and a pony
7023                    List<PackageInfo> apps =
7024                            PackageManagerBackupAgent.getStorableApplications(mPackageManager);
7025                    filterSet = packagesToNames(apps);
7026                    if (DEBUG) {
7027                        Slog.i(TAG, "Full restore; asking for " + filterSet.length + " apps");
7028                    }
7029                }
7030
7031                mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
7032
7033                // Pro tem, we insist on moving the settings provider package to last place.
7034                // Keep track of whether it's in the list, and bump it down if so.  We also
7035                // want to do the system package itself first if it's called for.
7036                boolean hasSystem = false;
7037                boolean hasSettings = false;
7038                for (int i = 0; i < filterSet.length; i++) {
7039                    try {
7040                        PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
7041                        if ("android".equals(info.packageName)) {
7042                            hasSystem = true;
7043                            continue;
7044                        }
7045                        if (SETTINGS_PACKAGE.equals(info.packageName)) {
7046                            hasSettings = true;
7047                            continue;
7048                        }
7049
7050                        if (appIsEligibleForBackup(info.applicationInfo)) {
7051                            mAcceptSet.add(info);
7052                        }
7053                    } catch (NameNotFoundException e) {
7054                        // requested package name doesn't exist; ignore it
7055                    }
7056                }
7057                if (hasSystem) {
7058                    try {
7059                        mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
7060                    } catch (NameNotFoundException e) {
7061                        // won't happen; we know a priori that it's valid
7062                    }
7063                }
7064                if (hasSettings) {
7065                    try {
7066                        mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
7067                    } catch (NameNotFoundException e) {
7068                        // this one is always valid too
7069                    }
7070                }
7071            }
7072
7073            if (MORE_DEBUG) {
7074                Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
7075                for (PackageInfo info : mAcceptSet) {
7076                    Slog.v(TAG, "   " + info.packageName);
7077                }
7078            }
7079        }
7080
7081        private String[] packagesToNames(List<PackageInfo> apps) {
7082            final int N = apps.size();
7083            String[] names = new String[N];
7084            for (int i = 0; i < N; i++) {
7085                names[i] = apps.get(i).packageName;
7086            }
7087            return names;
7088        }
7089
7090        // Execute one tick of whatever state machine the task implements
7091        @Override
7092        public void execute() {
7093            if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState);
7094            switch (mState) {
7095                case INITIAL:
7096                    startRestore();
7097                    break;
7098
7099                case RUNNING_QUEUE:
7100                    dispatchNextRestore();
7101                    break;
7102
7103                case RESTORE_KEYVALUE:
7104                    restoreKeyValue();
7105                    break;
7106
7107                case RESTORE_FULL:
7108                    restoreFull();
7109                    break;
7110
7111                case RESTORE_FINISHED:
7112                    restoreFinished();
7113                    break;
7114
7115                case FINAL:
7116                    if (!mFinished) finalizeRestore();
7117                    else {
7118                        Slog.e(TAG, "Duplicate finish");
7119                    }
7120                    mFinished = true;
7121                    break;
7122            }
7123        }
7124
7125        /*
7126         * SKETCH OF OPERATION
7127         *
7128         * create one of these PerformUnifiedRestoreTask objects, telling it which
7129         * dataset & transport to address, and then parameters within the restore
7130         * operation: single target package vs many, etc.
7131         *
7132         * 1. transport.startRestore(token, list-of-packages).  If we need @pm@  it is
7133         * always placed first and the settings provider always placed last [for now].
7134         *
7135         * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline]
7136         *
7137         *   [ state change => RUNNING_QUEUE ]
7138         *
7139         * NOW ITERATE:
7140         *
7141         * { 3. t.nextRestorePackage()
7142         *   4. does the metadata for this package allow us to restore it?
7143         *      does the on-disk app permit us to restore it? [re-check allowBackup etc]
7144         *   5. is this a key/value dataset?  => key/value agent restore
7145         *       [ state change => RESTORE_KEYVALUE ]
7146         *       5a. spin up agent
7147         *       5b. t.getRestoreData() to stage it properly
7148         *       5c. call into agent to perform restore
7149         *       5d. tear down agent
7150         *       [ state change => RUNNING_QUEUE ]
7151         *
7152         *   6. else it's a stream dataset:
7153         *       [ state change => RESTORE_FULL ]
7154         *       6a. instantiate the engine for a stream restore: engine handles agent lifecycles
7155         *       6b. spin off engine runner on separate thread
7156         *       6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket
7157         *       [ state change => RUNNING_QUEUE ]
7158         * }
7159         *
7160         *   [ state change => FINAL ]
7161         *
7162         * 7. t.finishRestore(), release wakelock, etc.
7163         *
7164         *
7165         */
7166
7167        // state INITIAL : set up for the restore and read the metadata if necessary
7168        private  void startRestore() {
7169            sendStartRestore(mAcceptSet.size());
7170
7171            // If we're starting a full-system restore, set up to begin widget ID remapping
7172            if (mIsSystemRestore) {
7173                AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER);
7174            }
7175
7176            try {
7177                String transportDir = mTransport.transportDirName();
7178                mStateDir = new File(mBaseStateDir, transportDir);
7179
7180                // Fetch the current metadata from the dataset first
7181                PackageInfo pmPackage = new PackageInfo();
7182                pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7183                mAcceptSet.add(0, pmPackage);
7184
7185                PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
7186                mStatus = mTransport.startRestore(mToken, packages);
7187                if (mStatus != BackupTransport.TRANSPORT_OK) {
7188                    Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
7189                    mStatus = BackupTransport.TRANSPORT_ERROR;
7190                    executeNextState(UnifiedRestoreState.FINAL);
7191                    return;
7192                }
7193
7194                RestoreDescription desc = mTransport.nextRestorePackage();
7195                if (desc == null) {
7196                    Slog.e(TAG, "No restore metadata available; halting");
7197                    mStatus = BackupTransport.TRANSPORT_ERROR;
7198                    executeNextState(UnifiedRestoreState.FINAL);
7199                    return;
7200                }
7201                if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
7202                    Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
7203                    mStatus = BackupTransport.TRANSPORT_ERROR;
7204                    executeNextState(UnifiedRestoreState.FINAL);
7205                    return;
7206                }
7207
7208                // Pull the Package Manager metadata from the restore set first
7209                mCurrentPackage = new PackageInfo();
7210                mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7211                mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
7212                mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
7213                if (MORE_DEBUG) {
7214                    Slog.v(TAG, "initiating restore for PMBA");
7215                }
7216                initiateOneRestore(mCurrentPackage, 0);
7217                // The PM agent called operationComplete() already, because our invocation
7218                // of it is process-local and therefore synchronous.  That means that the
7219                // next-state message (RUNNING_QUEUE) is already enqueued.  Only if we're
7220                // unable to proceed with running the queue do we remove that pending
7221                // message and jump straight to the FINAL state.
7222
7223                // Verify that the backup set includes metadata.  If not, we can't do
7224                // signature/version verification etc, so we simply do not proceed with
7225                // the restore operation.
7226                if (!mPmAgent.hasMetadata()) {
7227                    Slog.e(TAG, "No restore metadata available, so not restoring");
7228                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7229                            PACKAGE_MANAGER_SENTINEL,
7230                            "Package manager restore metadata missing");
7231                    mStatus = BackupTransport.TRANSPORT_ERROR;
7232                    mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7233                    executeNextState(UnifiedRestoreState.FINAL);
7234                    return;
7235                }
7236
7237                // Success; cache the metadata and continue as expected with the
7238                // next state already enqueued
7239
7240            } catch (RemoteException e) {
7241                // If we lost the transport at any time, halt
7242                Slog.e(TAG, "Unable to contact transport for restore");
7243                mStatus = BackupTransport.TRANSPORT_ERROR;
7244                mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7245                executeNextState(UnifiedRestoreState.FINAL);
7246                return;
7247            }
7248        }
7249
7250        // state RUNNING_QUEUE : figure out what the next thing to be restored is,
7251        // and fire the appropriate next step
7252        private void dispatchNextRestore() {
7253            UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
7254            try {
7255                mRestoreDescription = mTransport.nextRestorePackage();
7256                final String pkgName = (mRestoreDescription != null)
7257                        ? mRestoreDescription.getPackageName() : null;
7258                if (pkgName == null) {
7259                    Slog.e(TAG, "Failure getting next package name");
7260                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7261                    nextState = UnifiedRestoreState.FINAL;
7262                    return;
7263                } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) {
7264                    // Yay we've reached the end cleanly
7265                    if (DEBUG) {
7266                        Slog.v(TAG, "No more packages; finishing restore");
7267                    }
7268                    int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
7269                    EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
7270                    nextState = UnifiedRestoreState.FINAL;
7271                    return;
7272                }
7273
7274                if (DEBUG) {
7275                    Slog.i(TAG, "Next restore package: " + mRestoreDescription);
7276                }
7277                sendOnRestorePackage(pkgName);
7278
7279                Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName);
7280                if (metaInfo == null) {
7281                    Slog.e(TAG, "No metadata for " + pkgName);
7282                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
7283                            "Package metadata missing");
7284                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7285                    return;
7286                }
7287
7288                try {
7289                    mCurrentPackage = mPackageManager.getPackageInfo(
7290                            pkgName, PackageManager.GET_SIGNATURES);
7291                } catch (NameNotFoundException e) {
7292                    // Whoops, we thought we could restore this package but it
7293                    // turns out not to be present.  Skip it.
7294                    Slog.e(TAG, "Package not present: " + pkgName);
7295                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
7296                            "Package missing on device");
7297                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7298                    return;
7299                }
7300
7301                if (metaInfo.versionCode > mCurrentPackage.versionCode) {
7302                    // Data is from a "newer" version of the app than we have currently
7303                    // installed.  If the app has not declared that it is prepared to
7304                    // handle this case, we do not attempt the restore.
7305                    if ((mCurrentPackage.applicationInfo.flags
7306                            & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
7307                        String message = "Version " + metaInfo.versionCode
7308                                + " > installed version " + mCurrentPackage.versionCode;
7309                        Slog.w(TAG, "Package " + pkgName + ": " + message);
7310                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7311                                pkgName, message);
7312                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
7313                        return;
7314                    } else {
7315                        if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
7316                                + " > installed " + mCurrentPackage.versionCode
7317                                + " but restoreAnyVersion");
7318                    }
7319                }
7320
7321                if (DEBUG) Slog.v(TAG, "Package " + pkgName
7322                        + " restore version [" + metaInfo.versionCode
7323                        + "] is compatible with installed version ["
7324                        + mCurrentPackage.versionCode + "]");
7325
7326                // Reset per-package preconditions and fire the appropriate next state
7327                mWidgetData = null;
7328                final int type = mRestoreDescription.getDataType();
7329                if (type == RestoreDescription.TYPE_KEY_VALUE) {
7330                    nextState = UnifiedRestoreState.RESTORE_KEYVALUE;
7331                } else if (type == RestoreDescription.TYPE_FULL_STREAM) {
7332                    nextState = UnifiedRestoreState.RESTORE_FULL;
7333                } else {
7334                    // Unknown restore type; ignore this package and move on
7335                    Slog.e(TAG, "Unrecognized restore type " + type);
7336                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7337                    return;
7338                }
7339            } catch (RemoteException e) {
7340                Slog.e(TAG, "Can't get next target from transport; ending restore");
7341                EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7342                nextState = UnifiedRestoreState.FINAL;
7343                return;
7344            } finally {
7345                executeNextState(nextState);
7346            }
7347        }
7348
7349        // state RESTORE_KEYVALUE : restore one package via key/value API set
7350        private void restoreKeyValue() {
7351            // Initiating the restore will pass responsibility for the state machine's
7352            // progress to the agent callback, so we do not always execute the
7353            // next state here.
7354            final String packageName = mCurrentPackage.packageName;
7355            // Validate some semantic requirements that apply in this way
7356            // only to the key/value restore API flow
7357            if (mCurrentPackage.applicationInfo.backupAgentName == null
7358                    || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
7359                if (DEBUG) {
7360                    Slog.i(TAG, "Data exists for package " + packageName
7361                            + " but app has no agent; skipping");
7362                }
7363                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7364                        "Package has no agent");
7365                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7366                return;
7367            }
7368
7369            Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
7370            if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
7371                Slog.w(TAG, "Signature mismatch restoring " + packageName);
7372                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7373                        "Signature mismatch");
7374                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7375                return;
7376            }
7377
7378            // Good to go!  Set up and bind the agent...
7379            mAgent = bindToAgentSynchronous(
7380                    mCurrentPackage.applicationInfo,
7381                    IApplicationThread.BACKUP_MODE_INCREMENTAL);
7382            if (mAgent == null) {
7383                Slog.w(TAG, "Can't find backup agent for " + packageName);
7384                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7385                        "Restore agent missing");
7386                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7387                return;
7388            }
7389
7390            // And then finally start the restore on this agent
7391            try {
7392                initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
7393                ++mCount;
7394            } catch (Exception e) {
7395                Slog.e(TAG, "Error when attempting restore: " + e.toString());
7396                keyValueAgentErrorCleanup();
7397                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7398            }
7399        }
7400
7401        // Guts of a key/value restore operation
7402        void initiateOneRestore(PackageInfo app, int appVersionCode) {
7403            final String packageName = app.packageName;
7404
7405            if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
7406
7407            // !!! TODO: get the dirs from the transport
7408            mBackupDataName = new File(mDataDir, packageName + ".restore");
7409            mStageName = new File(mDataDir, packageName + ".stage");
7410            mNewStateName = new File(mStateDir, packageName + ".new");
7411            mSavedStateName = new File(mStateDir, packageName);
7412
7413            // don't stage the 'android' package where the wallpaper data lives.  this is
7414            // an optimization: we know there's no widget data hosted/published by that
7415            // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
7416            // data following the download.
7417            boolean staging = !packageName.equals("android");
7418            ParcelFileDescriptor stage;
7419            File downloadFile = (staging) ? mStageName : mBackupDataName;
7420
7421            final int token = generateToken();
7422            try {
7423                // Run the transport's restore pass
7424                stage = ParcelFileDescriptor.open(downloadFile,
7425                        ParcelFileDescriptor.MODE_READ_WRITE |
7426                        ParcelFileDescriptor.MODE_CREATE |
7427                        ParcelFileDescriptor.MODE_TRUNCATE);
7428
7429                if (!SELinux.restorecon(mBackupDataName)) {
7430                    Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
7431                }
7432
7433                if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
7434                    // Transport-level failure, so we wind everything up and
7435                    // terminate the restore operation.
7436                    Slog.e(TAG, "Error getting restore data for " + packageName);
7437                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7438                    stage.close();
7439                    downloadFile.delete();
7440                    executeNextState(UnifiedRestoreState.FINAL);
7441                    return;
7442                }
7443
7444                // We have the data from the transport. Now we extract and strip
7445                // any per-package metadata (typically widget-related information)
7446                // if appropriate
7447                if (staging) {
7448                    stage.close();
7449                    stage = ParcelFileDescriptor.open(downloadFile,
7450                            ParcelFileDescriptor.MODE_READ_ONLY);
7451
7452                    mBackupData = ParcelFileDescriptor.open(mBackupDataName,
7453                            ParcelFileDescriptor.MODE_READ_WRITE |
7454                            ParcelFileDescriptor.MODE_CREATE |
7455                            ParcelFileDescriptor.MODE_TRUNCATE);
7456
7457                    BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
7458                    BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
7459                    byte[] buffer = new byte[8192]; // will grow when needed
7460                    while (in.readNextHeader()) {
7461                        final String key = in.getKey();
7462                        final int size = in.getDataSize();
7463
7464                        // is this a special key?
7465                        if (key.equals(KEY_WIDGET_STATE)) {
7466                            if (DEBUG) {
7467                                Slog.i(TAG, "Restoring widget state for " + packageName);
7468                            }
7469                            mWidgetData = new byte[size];
7470                            in.readEntityData(mWidgetData, 0, size);
7471                        } else {
7472                            if (size > buffer.length) {
7473                                buffer = new byte[size];
7474                            }
7475                            in.readEntityData(buffer, 0, size);
7476                            out.writeEntityHeader(key, size);
7477                            out.writeEntityData(buffer, size);
7478                        }
7479                    }
7480
7481                    mBackupData.close();
7482                }
7483
7484                // Okay, we have the data.  Now have the agent do the restore.
7485                stage.close();
7486                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
7487                        ParcelFileDescriptor.MODE_READ_ONLY);
7488
7489                mNewState = ParcelFileDescriptor.open(mNewStateName,
7490                        ParcelFileDescriptor.MODE_READ_WRITE |
7491                        ParcelFileDescriptor.MODE_CREATE |
7492                        ParcelFileDescriptor.MODE_TRUNCATE);
7493
7494                // Kick off the restore, checking for hung agents.  The timeout or
7495                // the operationComplete() callback will schedule the next step,
7496                // so we do not do that here.
7497                prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
7498                mAgent.doRestore(mBackupData, appVersionCode, mNewState,
7499                        token, mBackupManagerBinder);
7500            } catch (Exception e) {
7501                Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
7502                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7503                        packageName, e.toString());
7504                keyValueAgentErrorCleanup();    // clears any pending timeout messages as well
7505
7506                // After a restore failure we go back to running the queue.  If there
7507                // are no more packages to be restored that will be handled by the
7508                // next step.
7509                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7510            }
7511        }
7512
7513        // state RESTORE_FULL : restore one package via streaming engine
7514        private void restoreFull() {
7515            // None of this can run on the work looper here, so we spin asynchronous
7516            // work like this:
7517            //
7518            //   StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk()
7519            //                       write it into the pipe to the engine
7520            //   EngineThread: FullRestoreEngine thread communicating with the target app
7521            //
7522            // When finished, StreamFeederThread executes next state as appropriate on the
7523            // backup looper, and the overall unified restore task resumes
7524            try {
7525                StreamFeederThread feeder = new StreamFeederThread();
7526                if (DEBUG) {
7527                    Slog.i(TAG, "Spinning threads for stream restore of "
7528                            + mCurrentPackage.packageName);
7529                }
7530                new Thread(feeder, "unified-stream-feeder").start();
7531
7532                // At this point the feeder is responsible for advancing the restore
7533                // state, so we're done here.
7534            } catch (IOException e) {
7535                // Unable to instantiate the feeder thread -- we need to bail on the
7536                // current target.  We haven't asked the transport for data yet, though,
7537                // so we can do that simply by going back to running the restore queue.
7538                Slog.e(TAG, "Unable to construct pipes for stream restore!");
7539                executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7540            }
7541        }
7542
7543        // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
7544        private void restoreFinished() {
7545            try {
7546                final int token = generateToken();
7547                prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
7548                mAgent.doRestoreFinished(token, mBackupManagerBinder);
7549                // If we get this far, the callback or timeout will schedule the
7550                // next restore state, so we're done
7551            } catch (Exception e) {
7552                Slog.e(TAG, "Unable to finalize restore of " + mCurrentPackage.packageName);
7553                executeNextState(UnifiedRestoreState.FINAL);
7554            }
7555        }
7556
7557        class StreamFeederThread extends RestoreEngine implements Runnable {
7558            final String TAG = "StreamFeederThread";
7559            FullRestoreEngine mEngine;
7560
7561            // pipe through which we read data from the transport. [0] read, [1] write
7562            ParcelFileDescriptor[] mTransportPipes;
7563
7564            // pipe through which the engine will read data.  [0] read, [1] write
7565            ParcelFileDescriptor[] mEnginePipes;
7566
7567            public StreamFeederThread() throws IOException {
7568                mTransportPipes = ParcelFileDescriptor.createPipe();
7569                mEnginePipes = ParcelFileDescriptor.createPipe();
7570                setRunning(true);
7571            }
7572
7573            @Override
7574            public void run() {
7575                UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
7576                int status = BackupTransport.TRANSPORT_OK;
7577
7578                EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
7579                        mCurrentPackage.packageName);
7580
7581                mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false);
7582                EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]);
7583
7584                ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
7585                ParcelFileDescriptor tReadEnd = mTransportPipes[0];
7586                ParcelFileDescriptor tWriteEnd = mTransportPipes[1];
7587
7588                int bufferSize = 32 * 1024;
7589                byte[] buffer = new byte[bufferSize];
7590                FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor());
7591                FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor());
7592
7593                // spin up the engine and start moving data to it
7594                new Thread(eThread, "unified-restore-engine").start();
7595
7596                try {
7597                    while (status == BackupTransport.TRANSPORT_OK) {
7598                        // have the transport write some of the restoring data to us
7599                        int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd);
7600                        if (result > 0) {
7601                            // The transport wrote this many bytes of restore data to the
7602                            // pipe, so pass it along to the engine.
7603                            if (MORE_DEBUG) {
7604                                Slog.v(TAG, "  <- transport provided chunk size " + result);
7605                            }
7606                            if (result > bufferSize) {
7607                                bufferSize = result;
7608                                buffer = new byte[bufferSize];
7609                            }
7610                            int toCopy = result;
7611                            while (toCopy > 0) {
7612                                int n = transportIn.read(buffer, 0, toCopy);
7613                                engineOut.write(buffer, 0, n);
7614                                toCopy -= n;
7615                                if (MORE_DEBUG) {
7616                                    Slog.v(TAG, "  -> wrote " + n + " to engine, left=" + toCopy);
7617                                }
7618                            }
7619                        } else if (result == BackupTransport.NO_MORE_DATA) {
7620                            // Clean finish.  Wind up and we're done!
7621                            if (MORE_DEBUG) {
7622                                Slog.i(TAG, "Got clean full-restore EOF for "
7623                                        + mCurrentPackage.packageName);
7624                            }
7625                            status = BackupTransport.TRANSPORT_OK;
7626                            break;
7627                        } else {
7628                            // Transport reported some sort of failure; the fall-through
7629                            // handling will deal properly with that.
7630                            Slog.e(TAG, "Error " + result + " streaming restore for "
7631                                    + mCurrentPackage.packageName);
7632                            EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7633                            status = result;
7634                        }
7635                    }
7636                    if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through");
7637                } catch (IOException e) {
7638                    // We lost our ability to communicate via the pipes.  That's worrying
7639                    // but potentially recoverable; abandon this package's restore but
7640                    // carry on with the next restore target.
7641                    Slog.e(TAG, "Unable to route data for restore");
7642                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7643                            mCurrentPackage.packageName, "I/O error on pipes");
7644                    status = BackupTransport.AGENT_ERROR;
7645                } catch (RemoteException e) {
7646                    // The transport went away; terminate the whole operation.  Closing
7647                    // the sockets will wake up the engine and it will then tidy up the
7648                    // remote end.
7649                    Slog.e(TAG, "Transport failed during restore");
7650                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7651                    status = BackupTransport.TRANSPORT_ERROR;
7652                } finally {
7653                    // Close the transport pipes and *our* end of the engine pipe,
7654                    // but leave the engine thread's end open so that it properly
7655                    // hits EOF and winds up its operations.
7656                    IoUtils.closeQuietly(mEnginePipes[1]);
7657                    IoUtils.closeQuietly(mTransportPipes[0]);
7658                    IoUtils.closeQuietly(mTransportPipes[1]);
7659
7660                    // Don't proceed until the engine has torn down the agent etc
7661                    eThread.waitForResult();
7662
7663                    if (MORE_DEBUG) {
7664                        Slog.i(TAG, "engine thread finished; proceeding");
7665                    }
7666
7667                    // Now we're really done with this one too
7668                    IoUtils.closeQuietly(mEnginePipes[0]);
7669
7670                    // If we hit a transport-level error, we are done with everything;
7671                    // if we hit an agent error we just go back to running the queue.
7672                    if (status == BackupTransport.TRANSPORT_OK) {
7673                        // Clean finish, so just carry on
7674                        nextState = UnifiedRestoreState.RUNNING_QUEUE;
7675                    } else {
7676                        // Something went wrong somewhere.  Whether it was at the transport
7677                        // level is immaterial; we need to tell the transport to bail
7678                        try {
7679                            mTransport.abortFullRestore();
7680                        } catch (RemoteException e) {
7681                            // transport itself is dead; make sure we handle this as a
7682                            // fatal error
7683                            status = BackupTransport.TRANSPORT_ERROR;
7684                        }
7685
7686                        // We also need to wipe the current target's data, as it's probably
7687                        // in an incoherent state.
7688                        clearApplicationDataSynchronous(mCurrentPackage.packageName);
7689
7690                        // Schedule the next state based on the nature of our failure
7691                        if (status == BackupTransport.TRANSPORT_ERROR) {
7692                            nextState = UnifiedRestoreState.FINAL;
7693                        } else {
7694                            nextState = UnifiedRestoreState.RUNNING_QUEUE;
7695                        }
7696                    }
7697                    executeNextState(nextState);
7698                    setRunning(false);
7699                }
7700            }
7701
7702        }
7703
7704        class EngineThread implements Runnable {
7705            FullRestoreEngine mEngine;
7706            FileInputStream mEngineStream;
7707
7708            EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
7709                mEngine = engine;
7710                engine.setRunning(true);
7711                mEngineStream = new FileInputStream(engineSocket.getFileDescriptor());
7712            }
7713
7714            public boolean isRunning() {
7715                return mEngine.isRunning();
7716            }
7717
7718            public int waitForResult() {
7719                return mEngine.waitForResult();
7720            }
7721
7722            @Override
7723            public void run() {
7724                while (mEngine.isRunning()) {
7725                    mEngine.restoreOneFile(mEngineStream);
7726                }
7727            }
7728        }
7729
7730        // state FINAL : tear everything down and we're done.
7731        private void finalizeRestore() {
7732            if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
7733
7734            try {
7735                mTransport.finishRestore();
7736            } catch (Exception e) {
7737                Slog.e(TAG, "Error finishing restore", e);
7738            }
7739
7740            // Tell the observer we're done
7741            if (mObserver != null) {
7742                try {
7743                    mObserver.restoreFinished(mStatus);
7744                } catch (RemoteException e) {
7745                    Slog.d(TAG, "Restore observer died at restoreFinished");
7746                }
7747            }
7748
7749            // Clear any ongoing session timeout.
7750            mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
7751
7752            // If we have a PM token, we must under all circumstances be sure to
7753            // handshake when we've finished.
7754            if (mPmToken > 0) {
7755                if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
7756                try {
7757                    mPackageManagerBinder.finishPackageInstall(mPmToken);
7758                } catch (RemoteException e) { /* can't happen */ }
7759            } else {
7760                // We were invoked via an active restore session, not by the Package
7761                // Manager, so start up the session timeout again.
7762                mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
7763                        TIMEOUT_RESTORE_INTERVAL);
7764            }
7765
7766            // Kick off any work that may be needed regarding app widget restores
7767            AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER);
7768
7769            // If this was a full-system restore, record the ancestral
7770            // dataset information
7771            if (mIsSystemRestore && mPmAgent != null) {
7772                mAncestralPackages = mPmAgent.getRestoredPackages();
7773                mAncestralToken = mToken;
7774                writeRestoreTokens();
7775            }
7776
7777            // done; we can finally release the wakelock and be legitimately done.
7778            Slog.i(TAG, "Restore complete.");
7779            mWakelock.release();
7780        }
7781
7782        void keyValueAgentErrorCleanup() {
7783            // If the agent fails restore, it might have put the app's data
7784            // into an incoherent state.  For consistency we wipe its data
7785            // again in this case before continuing with normal teardown
7786            clearApplicationDataSynchronous(mCurrentPackage.packageName);
7787            keyValueAgentCleanup();
7788        }
7789
7790        void keyValueAgentCleanup() {
7791            mBackupDataName.delete();
7792            mStageName.delete();
7793            try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
7794            try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
7795            mBackupData = mNewState = null;
7796
7797            // if everything went okay, remember the recorded state now
7798            //
7799            // !!! TODO: the restored data could be migrated on the server
7800            // side into the current dataset.  In that case the new state file
7801            // we just created would reflect the data already extant in the
7802            // backend, so there'd be nothing more to do.  Until that happens,
7803            // however, we need to make sure that we record the data to the
7804            // current backend dataset.  (Yes, this means shipping the data over
7805            // the wire in both directions.  That's bad, but consistency comes
7806            // first, then efficiency.)  Once we introduce server-side data
7807            // migration to the newly-restored device's dataset, we will change
7808            // the following from a discard of the newly-written state to the
7809            // "correct" operation of renaming into the canonical state blob.
7810            mNewStateName.delete();                      // TODO: remove; see above comment
7811            //mNewStateName.renameTo(mSavedStateName);   // TODO: replace with this
7812
7813            // If this wasn't the PM pseudopackage, tear down the agent side
7814            if (mCurrentPackage.applicationInfo != null) {
7815                // unbind and tidy up even on timeout or failure
7816                try {
7817                    mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
7818
7819                    // The agent was probably running with a stub Application object,
7820                    // which isn't a valid run mode for the main app logic.  Shut
7821                    // down the app so that next time it's launched, it gets the
7822                    // usual full initialization.  Note that this is only done for
7823                    // full-system restores: when a single app has requested a restore,
7824                    // it is explicitly not killed following that operation.
7825                    if (mTargetPackage == null && (mCurrentPackage.applicationInfo.flags
7826                            & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
7827                        if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
7828                                + mCurrentPackage.applicationInfo.processName);
7829                        mActivityManager.killApplicationProcess(
7830                                mCurrentPackage.applicationInfo.processName,
7831                                mCurrentPackage.applicationInfo.uid);
7832                    }
7833                } catch (RemoteException e) {
7834                    // can't happen; we run in the same process as the activity manager
7835                }
7836            }
7837
7838            // The caller is responsible for reestablishing the state machine; our
7839            // responsibility here is to clear the decks for whatever comes next.
7840            mBackupHandler.removeMessages(MSG_TIMEOUT, this);
7841            synchronized (mCurrentOpLock) {
7842                mCurrentOperations.clear();
7843            }
7844        }
7845
7846        @Override
7847        public void operationComplete() {
7848            if (MORE_DEBUG) {
7849                Slog.i(TAG, "operationComplete() during restore: target="
7850                        + mCurrentPackage.packageName
7851                        + " state=" + mState);
7852            }
7853
7854            final UnifiedRestoreState nextState;
7855            switch (mState) {
7856                case INITIAL:
7857                    // We've just (manually) restored the PMBA.  It doesn't need the
7858                    // additional restore-finished callback so we bypass that and go
7859                    // directly to running the queue.
7860                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7861                    break;
7862
7863                case RESTORE_KEYVALUE:
7864                case RESTORE_FULL: {
7865                    // Okay, we've just heard back from the agent that it's done with
7866                    // the restore itself.  We now have to send the same agent its
7867                    // doRestoreFinished() callback, so roll into that state.
7868                    nextState = UnifiedRestoreState.RESTORE_FINISHED;
7869                    break;
7870                }
7871
7872                case RESTORE_FINISHED: {
7873                    // Okay, we're done with this package.  Tidy up and go on to the next
7874                    // app in the queue.
7875                    int size = (int) mBackupDataName.length();
7876                    EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
7877                            mCurrentPackage.packageName, size);
7878
7879                    // Just go back to running the restore queue
7880                    keyValueAgentCleanup();
7881
7882                    // If there was widget state associated with this app, get the OS to
7883                    // incorporate it into current bookeeping and then pass that along to
7884                    // the app as part of the restore-time work.
7885                    if (mWidgetData != null) {
7886                        restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
7887                    }
7888
7889                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
7890                    break;
7891                }
7892
7893                default: {
7894                    // Some kind of horrible semantic error; we're in an unexpected state.
7895                    // Back off hard and wind up.
7896                    Slog.e(TAG, "Unexpected restore callback into state " + mState);
7897                    keyValueAgentErrorCleanup();
7898                    nextState = UnifiedRestoreState.FINAL;
7899                    break;
7900                }
7901            }
7902
7903            executeNextState(nextState);
7904        }
7905
7906        // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
7907        @Override
7908        public void handleTimeout() {
7909            Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
7910            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7911                    mCurrentPackage.packageName, "restore timeout");
7912            // Handle like an agent that threw on invocation: wipe it and go on to the next
7913            keyValueAgentErrorCleanup();
7914            executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7915        }
7916
7917        void executeNextState(UnifiedRestoreState nextState) {
7918            if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
7919                    + this + " nextState=" + nextState);
7920            mState = nextState;
7921            Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
7922            mBackupHandler.sendMessage(msg);
7923        }
7924
7925        // restore observer support
7926        void sendStartRestore(int numPackages) {
7927            if (mObserver != null) {
7928                try {
7929                    mObserver.restoreStarting(numPackages);
7930                } catch (RemoteException e) {
7931                    Slog.w(TAG, "Restore observer went away: startRestore");
7932                    mObserver = null;
7933                }
7934            }
7935        }
7936
7937        void sendOnRestorePackage(String name) {
7938            if (mObserver != null) {
7939                if (mObserver != null) {
7940                    try {
7941                        mObserver.onUpdate(mCount, name);
7942                    } catch (RemoteException e) {
7943                        Slog.d(TAG, "Restore observer died in onUpdate");
7944                        mObserver = null;
7945                    }
7946                }
7947            }
7948        }
7949
7950        void sendEndRestore() {
7951            if (mObserver != null) {
7952                try {
7953                    mObserver.restoreFinished(mStatus);
7954                } catch (RemoteException e) {
7955                    Slog.w(TAG, "Restore observer went away: endRestore");
7956                    mObserver = null;
7957                }
7958            }
7959        }
7960    }
7961
7962    class PerformClearTask implements Runnable {
7963        IBackupTransport mTransport;
7964        PackageInfo mPackage;
7965
7966        PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
7967            mTransport = transport;
7968            mPackage = packageInfo;
7969        }
7970
7971        public void run() {
7972            try {
7973                // Clear the on-device backup state to ensure a full backup next time
7974                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
7975                File stateFile = new File(stateDir, mPackage.packageName);
7976                stateFile.delete();
7977
7978                // Tell the transport to remove all the persistent storage for the app
7979                // TODO - need to handle failures
7980                mTransport.clearBackupData(mPackage);
7981            } catch (RemoteException e) {
7982                // can't happen; the transport is local
7983            } catch (Exception e) {
7984                Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
7985            } finally {
7986                try {
7987                    // TODO - need to handle failures
7988                    mTransport.finishBackup();
7989                } catch (RemoteException e) {
7990                    // can't happen; the transport is local
7991                }
7992
7993                // Last but not least, release the cpu
7994                mWakelock.release();
7995            }
7996        }
7997    }
7998
7999    class PerformInitializeTask implements Runnable {
8000        HashSet<String> mQueue;
8001
8002        PerformInitializeTask(HashSet<String> transportNames) {
8003            mQueue = transportNames;
8004        }
8005
8006        public void run() {
8007            try {
8008                for (String transportName : mQueue) {
8009                    IBackupTransport transport = getTransport(transportName);
8010                    if (transport == null) {
8011                        Slog.e(TAG, "Requested init for " + transportName + " but not found");
8012                        continue;
8013                    }
8014
8015                    Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
8016                    EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
8017                    long startRealtime = SystemClock.elapsedRealtime();
8018                    int status = transport.initializeDevice();
8019
8020                    if (status == BackupTransport.TRANSPORT_OK) {
8021                        status = transport.finishBackup();
8022                    }
8023
8024                    // Okay, the wipe really happened.  Clean up our local bookkeeping.
8025                    if (status == BackupTransport.TRANSPORT_OK) {
8026                        Slog.i(TAG, "Device init successful");
8027                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
8028                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
8029                        resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
8030                        EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
8031                        synchronized (mQueueLock) {
8032                            recordInitPendingLocked(false, transportName);
8033                        }
8034                    } else {
8035                        // If this didn't work, requeue this one and try again
8036                        // after a suitable interval
8037                        Slog.e(TAG, "Transport error in initializeDevice()");
8038                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
8039                        synchronized (mQueueLock) {
8040                            recordInitPendingLocked(true, transportName);
8041                        }
8042                        // do this via another alarm to make sure of the wakelock states
8043                        long delay = transport.requestBackupTime();
8044                        if (DEBUG) Slog.w(TAG, "init failed on "
8045                                + transportName + " resched in " + delay);
8046                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
8047                                System.currentTimeMillis() + delay, mRunInitIntent);
8048                    }
8049                }
8050            } catch (RemoteException e) {
8051                // can't happen; the transports are local
8052            } catch (Exception e) {
8053                Slog.e(TAG, "Unexpected error performing init", e);
8054            } finally {
8055                // Done; release the wakelock
8056                mWakelock.release();
8057            }
8058        }
8059    }
8060
8061    private void dataChangedImpl(String packageName) {
8062        HashSet<String> targets = dataChangedTargets(packageName);
8063        dataChangedImpl(packageName, targets);
8064    }
8065
8066    private void dataChangedImpl(String packageName, HashSet<String> targets) {
8067        // Record that we need a backup pass for the caller.  Since multiple callers
8068        // may share a uid, we need to note all candidates within that uid and schedule
8069        // a backup pass for each of them.
8070        if (targets == null) {
8071            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8072                   + " uid=" + Binder.getCallingUid());
8073            return;
8074        }
8075
8076        synchronized (mQueueLock) {
8077            // Note that this client has made data changes that need to be backed up
8078            if (targets.contains(packageName)) {
8079                // Add the caller to the set of pending backups.  If there is
8080                // one already there, then overwrite it, but no harm done.
8081                BackupRequest req = new BackupRequest(packageName);
8082                if (mPendingBackups.put(packageName, req) == null) {
8083                    if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
8084
8085                    // Journal this request in case of crash.  The put()
8086                    // operation returned null when this package was not already
8087                    // in the set; we want to avoid touching the disk redundantly.
8088                    writeToJournalLocked(packageName);
8089
8090                    if (MORE_DEBUG) {
8091                        int numKeys = mPendingBackups.size();
8092                        Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
8093                        for (BackupRequest b : mPendingBackups.values()) {
8094                            Slog.d(TAG, "    + " + b);
8095                        }
8096                    }
8097                }
8098            }
8099        }
8100    }
8101
8102    // Note: packageName is currently unused, but may be in the future
8103    private HashSet<String> dataChangedTargets(String packageName) {
8104        // If the caller does not hold the BACKUP permission, it can only request a
8105        // backup of its own data.
8106        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8107                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8108            synchronized (mBackupParticipants) {
8109                return mBackupParticipants.get(Binder.getCallingUid());
8110            }
8111        }
8112
8113        // a caller with full permission can ask to back up any participating app
8114        // !!! TODO: allow backup of ANY app?
8115        HashSet<String> targets = new HashSet<String>();
8116        synchronized (mBackupParticipants) {
8117            int N = mBackupParticipants.size();
8118            for (int i = 0; i < N; i++) {
8119                HashSet<String> s = mBackupParticipants.valueAt(i);
8120                if (s != null) {
8121                    targets.addAll(s);
8122                }
8123            }
8124        }
8125        return targets;
8126    }
8127
8128    private void writeToJournalLocked(String str) {
8129        RandomAccessFile out = null;
8130        try {
8131            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
8132            out = new RandomAccessFile(mJournal, "rws");
8133            out.seek(out.length());
8134            out.writeUTF(str);
8135        } catch (IOException e) {
8136            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
8137            mJournal = null;
8138        } finally {
8139            try { if (out != null) out.close(); } catch (IOException e) {}
8140        }
8141    }
8142
8143    // ----- IBackupManager binder interface -----
8144
8145    public void dataChanged(final String packageName) {
8146        final int callingUserHandle = UserHandle.getCallingUserId();
8147        if (callingUserHandle != UserHandle.USER_OWNER) {
8148            // App is running under a non-owner user profile.  For now, we do not back
8149            // up data from secondary user profiles.
8150            // TODO: backups for all user profiles.
8151            if (MORE_DEBUG) {
8152                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
8153                        + callingUserHandle);
8154            }
8155            return;
8156        }
8157
8158        final HashSet<String> targets = dataChangedTargets(packageName);
8159        if (targets == null) {
8160            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8161                   + " uid=" + Binder.getCallingUid());
8162            return;
8163        }
8164
8165        mBackupHandler.post(new Runnable() {
8166                public void run() {
8167                    dataChangedImpl(packageName, targets);
8168                }
8169            });
8170    }
8171
8172    // Clear the given package's backup data from the current transport
8173    public void clearBackupData(String transportName, String packageName) {
8174        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
8175        PackageInfo info;
8176        try {
8177            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
8178        } catch (NameNotFoundException e) {
8179            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
8180            return;
8181        }
8182
8183        // If the caller does not hold the BACKUP permission, it can only request a
8184        // wipe of its own backed-up data.
8185        HashSet<String> apps;
8186        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8187                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8188            apps = mBackupParticipants.get(Binder.getCallingUid());
8189        } else {
8190            // a caller with full permission can ask to back up any participating app
8191            // !!! TODO: allow data-clear of ANY app?
8192            if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
8193            apps = new HashSet<String>();
8194            int N = mBackupParticipants.size();
8195            for (int i = 0; i < N; i++) {
8196                HashSet<String> s = mBackupParticipants.valueAt(i);
8197                if (s != null) {
8198                    apps.addAll(s);
8199                }
8200            }
8201        }
8202
8203        // Is the given app an available participant?
8204        if (apps.contains(packageName)) {
8205            // found it; fire off the clear request
8206            if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
8207            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
8208            synchronized (mQueueLock) {
8209                final IBackupTransport transport = getTransport(transportName);
8210                if (transport == null) {
8211                    // transport is currently unavailable -- make sure to retry
8212                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
8213                            new ClearRetryParams(transportName, packageName));
8214                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
8215                    return;
8216                }
8217                long oldId = Binder.clearCallingIdentity();
8218                mWakelock.acquire();
8219                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
8220                        new ClearParams(transport, info));
8221                mBackupHandler.sendMessage(msg);
8222                Binder.restoreCallingIdentity(oldId);
8223            }
8224        }
8225    }
8226
8227    // Run a backup pass immediately for any applications that have declared
8228    // that they have pending updates.
8229    public void backupNow() {
8230        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
8231
8232        if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
8233        synchronized (mQueueLock) {
8234            // Because the alarms we are using can jitter, and we want an *immediate*
8235            // backup pass to happen, we restart the timer beginning with "next time,"
8236            // then manually fire the backup trigger intent ourselves.
8237            startBackupAlarmsLocked(BACKUP_INTERVAL);
8238            try {
8239                mRunBackupIntent.send();
8240            } catch (PendingIntent.CanceledException e) {
8241                // should never happen
8242                Slog.e(TAG, "run-backup intent cancelled!");
8243            }
8244        }
8245    }
8246
8247    boolean deviceIsProvisioned() {
8248        final ContentResolver resolver = mContext.getContentResolver();
8249        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
8250    }
8251
8252    // Run a *full* backup pass for the given packages, writing the resulting data stream
8253    // to the supplied file descriptor.  This method is synchronous and does not return
8254    // to the caller until the backup has been completed.
8255    //
8256    // This is the variant used by 'adb backup'; it requires on-screen confirmation
8257    // by the user because it can be used to offload data over untrusted USB.
8258    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
8259            boolean includeObbs, boolean includeShared, boolean doWidgets,
8260            boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) {
8261        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
8262
8263        final int callingUserHandle = UserHandle.getCallingUserId();
8264        if (callingUserHandle != UserHandle.USER_OWNER) {
8265            throw new IllegalStateException("Backup supported only for the device owner");
8266        }
8267
8268        // Validate
8269        if (!doAllApps) {
8270            if (!includeShared) {
8271                // If we're backing up shared data (sdcard or equivalent), then we can run
8272                // without any supplied app names.  Otherwise, we'd be doing no work, so
8273                // report the error.
8274                if (pkgList == null || pkgList.length == 0) {
8275                    throw new IllegalArgumentException(
8276                            "Backup requested but neither shared nor any apps named");
8277                }
8278            }
8279        }
8280
8281        long oldId = Binder.clearCallingIdentity();
8282        try {
8283            // Doesn't make sense to do a full backup prior to setup
8284            if (!deviceIsProvisioned()) {
8285                Slog.i(TAG, "Full backup not supported before setup");
8286                return;
8287            }
8288
8289            if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
8290                    + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
8291                    + " system=" + includeSystem + " pkgs=" + pkgList);
8292            Slog.i(TAG, "Beginning full backup...");
8293
8294            FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
8295                    includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList);
8296            final int token = generateToken();
8297            synchronized (mFullConfirmations) {
8298                mFullConfirmations.put(token, params);
8299            }
8300
8301            // start up the confirmation UI
8302            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
8303            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
8304                Slog.e(TAG, "Unable to launch full backup confirmation");
8305                mFullConfirmations.delete(token);
8306                return;
8307            }
8308
8309            // make sure the screen is lit for the user interaction
8310            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
8311
8312            // start the confirmation countdown
8313            startConfirmationTimeout(token, params);
8314
8315            // wait for the backup to be performed
8316            if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
8317            waitForCompletion(params);
8318        } finally {
8319            try {
8320                fd.close();
8321            } catch (IOException e) {
8322                // just eat it
8323            }
8324            Binder.restoreCallingIdentity(oldId);
8325            Slog.d(TAG, "Full backup processing complete.");
8326        }
8327    }
8328
8329    public void fullTransportBackup(String[] pkgNames) {
8330        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
8331                "fullTransportBackup");
8332
8333        final int callingUserHandle = UserHandle.getCallingUserId();
8334        if (callingUserHandle != UserHandle.USER_OWNER) {
8335            throw new IllegalStateException("Restore supported only for the device owner");
8336        }
8337
8338        if (DEBUG) {
8339            Slog.d(TAG, "fullTransportBackup()");
8340        }
8341
8342        AtomicBoolean latch = new AtomicBoolean(false);
8343        PerformFullTransportBackupTask task =
8344                new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
8345        (new Thread(task, "full-transport-master")).start();
8346        synchronized (latch) {
8347            try {
8348                while (latch.get() == false) {
8349                    latch.wait();
8350                }
8351            } catch (InterruptedException e) {}
8352        }
8353        if (DEBUG) {
8354            Slog.d(TAG, "Done with full transport backup.");
8355        }
8356    }
8357
8358    public void fullRestore(ParcelFileDescriptor fd) {
8359        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
8360
8361        final int callingUserHandle = UserHandle.getCallingUserId();
8362        if (callingUserHandle != UserHandle.USER_OWNER) {
8363            throw new IllegalStateException("Restore supported only for the device owner");
8364        }
8365
8366        long oldId = Binder.clearCallingIdentity();
8367
8368        try {
8369            // Check whether the device has been provisioned -- we don't handle
8370            // full restores prior to completing the setup process.
8371            if (!deviceIsProvisioned()) {
8372                Slog.i(TAG, "Full restore not permitted before setup");
8373                return;
8374            }
8375
8376            Slog.i(TAG, "Beginning full restore...");
8377
8378            FullRestoreParams params = new FullRestoreParams(fd);
8379            final int token = generateToken();
8380            synchronized (mFullConfirmations) {
8381                mFullConfirmations.put(token, params);
8382            }
8383
8384            // start up the confirmation UI
8385            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
8386            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
8387                Slog.e(TAG, "Unable to launch full restore confirmation");
8388                mFullConfirmations.delete(token);
8389                return;
8390            }
8391
8392            // make sure the screen is lit for the user interaction
8393            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
8394
8395            // start the confirmation countdown
8396            startConfirmationTimeout(token, params);
8397
8398            // wait for the restore to be performed
8399            if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
8400            waitForCompletion(params);
8401        } finally {
8402            try {
8403                fd.close();
8404            } catch (IOException e) {
8405                Slog.w(TAG, "Error trying to close fd after full restore: " + e);
8406            }
8407            Binder.restoreCallingIdentity(oldId);
8408            Slog.i(TAG, "Full restore processing complete.");
8409        }
8410    }
8411
8412    boolean startConfirmationUi(int token, String action) {
8413        try {
8414            Intent confIntent = new Intent(action);
8415            confIntent.setClassName("com.android.backupconfirm",
8416                    "com.android.backupconfirm.BackupRestoreConfirmation");
8417            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
8418            confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
8419            mContext.startActivity(confIntent);
8420        } catch (ActivityNotFoundException e) {
8421            return false;
8422        }
8423        return true;
8424    }
8425
8426    void startConfirmationTimeout(int token, FullParams params) {
8427        if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
8428                + TIMEOUT_FULL_CONFIRMATION + " millis");
8429        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
8430                token, 0, params);
8431        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
8432    }
8433
8434    void waitForCompletion(FullParams params) {
8435        synchronized (params.latch) {
8436            while (params.latch.get() == false) {
8437                try {
8438                    params.latch.wait();
8439                } catch (InterruptedException e) { /* never interrupted */ }
8440            }
8441        }
8442    }
8443
8444    void signalFullBackupRestoreCompletion(FullParams params) {
8445        synchronized (params.latch) {
8446            params.latch.set(true);
8447            params.latch.notifyAll();
8448        }
8449    }
8450
8451    // Confirm that the previously-requested full backup/restore operation can proceed.  This
8452    // is used to require a user-facing disclosure about the operation.
8453    public void acknowledgeFullBackupOrRestore(int token, boolean allow,
8454            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
8455        if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
8456                + " allow=" + allow);
8457
8458        // TODO: possibly require not just this signature-only permission, but even
8459        // require that the specific designated confirmation-UI app uid is the caller?
8460        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
8461
8462        long oldId = Binder.clearCallingIdentity();
8463        try {
8464
8465            FullParams params;
8466            synchronized (mFullConfirmations) {
8467                params = mFullConfirmations.get(token);
8468                if (params != null) {
8469                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
8470                    mFullConfirmations.delete(token);
8471
8472                    if (allow) {
8473                        final int verb = params instanceof FullBackupParams
8474                                ? MSG_RUN_ADB_BACKUP
8475                                : MSG_RUN_ADB_RESTORE;
8476
8477                        params.observer = observer;
8478                        params.curPassword = curPassword;
8479
8480                        params.encryptPassword = encPpassword;
8481
8482                        if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
8483                        mWakelock.acquire();
8484                        Message msg = mBackupHandler.obtainMessage(verb, params);
8485                        mBackupHandler.sendMessage(msg);
8486                    } else {
8487                        Slog.w(TAG, "User rejected full backup/restore operation");
8488                        // indicate completion without having actually transferred any data
8489                        signalFullBackupRestoreCompletion(params);
8490                    }
8491                } else {
8492                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
8493                }
8494            }
8495        } finally {
8496            Binder.restoreCallingIdentity(oldId);
8497        }
8498    }
8499
8500    // Enable/disable backups
8501    public void setBackupEnabled(boolean enable) {
8502        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8503                "setBackupEnabled");
8504
8505        Slog.i(TAG, "Backup enabled => " + enable);
8506
8507        long oldId = Binder.clearCallingIdentity();
8508        try {
8509            boolean wasEnabled = mEnabled;
8510            synchronized (this) {
8511                Settings.Secure.putInt(mContext.getContentResolver(),
8512                        Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
8513                mEnabled = enable;
8514            }
8515
8516            synchronized (mQueueLock) {
8517                if (enable && !wasEnabled && mProvisioned) {
8518                    // if we've just been enabled, start scheduling backup passes
8519                    startBackupAlarmsLocked(BACKUP_INTERVAL);
8520                    scheduleNextFullBackupJob();
8521                } else if (!enable) {
8522                    // No longer enabled, so stop running backups
8523                    if (DEBUG) Slog.i(TAG, "Opting out of backup");
8524
8525                    mAlarmManager.cancel(mRunBackupIntent);
8526
8527                    // This also constitutes an opt-out, so we wipe any data for
8528                    // this device from the backend.  We start that process with
8529                    // an alarm in order to guarantee wakelock states.
8530                    if (wasEnabled && mProvisioned) {
8531                        // NOTE: we currently flush every registered transport, not just
8532                        // the currently-active one.
8533                        HashSet<String> allTransports;
8534                        synchronized (mTransports) {
8535                            allTransports = new HashSet<String>(mTransports.keySet());
8536                        }
8537                        // build the set of transports for which we are posting an init
8538                        for (String transport : allTransports) {
8539                            recordInitPendingLocked(true, transport);
8540                        }
8541                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
8542                                mRunInitIntent);
8543                    }
8544                }
8545            }
8546        } finally {
8547            Binder.restoreCallingIdentity(oldId);
8548        }
8549    }
8550
8551    // Enable/disable automatic restore of app data at install time
8552    public void setAutoRestore(boolean doAutoRestore) {
8553        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8554                "setAutoRestore");
8555
8556        Slog.i(TAG, "Auto restore => " + doAutoRestore);
8557
8558        final long oldId = Binder.clearCallingIdentity();
8559        try {
8560            synchronized (this) {
8561                Settings.Secure.putInt(mContext.getContentResolver(),
8562                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
8563                mAutoRestore = doAutoRestore;
8564            }
8565        } finally {
8566            Binder.restoreCallingIdentity(oldId);
8567        }
8568    }
8569
8570    // Mark the backup service as having been provisioned
8571    public void setBackupProvisioned(boolean available) {
8572        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8573                "setBackupProvisioned");
8574        /*
8575         * This is now a no-op; provisioning is simply the device's own setup state.
8576         */
8577    }
8578
8579    private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
8580        // We used to use setInexactRepeating(), but that may be linked to
8581        // backups running at :00 more often than not, creating load spikes.
8582        // Schedule at an exact time for now, and also add a bit of "fuzz".
8583
8584        Random random = new Random();
8585        long when = System.currentTimeMillis() + delayBeforeFirstBackup +
8586                random.nextInt(FUZZ_MILLIS);
8587        mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
8588                BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
8589        mNextBackupPass = when;
8590    }
8591
8592    // Report whether the backup mechanism is currently enabled
8593    public boolean isBackupEnabled() {
8594        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
8595        return mEnabled;    // no need to synchronize just to read it
8596    }
8597
8598    // Report the name of the currently active transport
8599    public String getCurrentTransport() {
8600        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8601                "getCurrentTransport");
8602        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
8603        return mCurrentTransport;
8604    }
8605
8606    // Report all known, available backup transports
8607    public String[] listAllTransports() {
8608        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
8609
8610        String[] list = null;
8611        ArrayList<String> known = new ArrayList<String>();
8612        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
8613            if (entry.getValue() != null) {
8614                known.add(entry.getKey());
8615            }
8616        }
8617
8618        if (known.size() > 0) {
8619            list = new String[known.size()];
8620            known.toArray(list);
8621        }
8622        return list;
8623    }
8624
8625    // Select which transport to use for the next backup operation.
8626    public String selectBackupTransport(String transport) {
8627        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8628                "selectBackupTransport");
8629
8630        synchronized (mTransports) {
8631            final long oldId = Binder.clearCallingIdentity();
8632            try {
8633                String prevTransport = mCurrentTransport;
8634                mCurrentTransport = transport;
8635                Settings.Secure.putString(mContext.getContentResolver(),
8636                        Settings.Secure.BACKUP_TRANSPORT, transport);
8637                Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
8638                        + " returning " + prevTransport);
8639                return prevTransport;
8640            } finally {
8641                Binder.restoreCallingIdentity(oldId);
8642            }
8643        }
8644    }
8645
8646    // Supply the configuration Intent for the given transport.  If the name is not one
8647    // of the available transports, or if the transport does not supply any configuration
8648    // UI, the method returns null.
8649    public Intent getConfigurationIntent(String transportName) {
8650        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8651                "getConfigurationIntent");
8652
8653        synchronized (mTransports) {
8654            final IBackupTransport transport = mTransports.get(transportName);
8655            if (transport != null) {
8656                try {
8657                    final Intent intent = transport.configurationIntent();
8658                    if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
8659                            + intent);
8660                    return intent;
8661                } catch (RemoteException e) {
8662                    /* fall through to return null */
8663                }
8664            }
8665        }
8666
8667        return null;
8668    }
8669
8670    // Supply the configuration summary string for the given transport.  If the name is
8671    // not one of the available transports, or if the transport does not supply any
8672    // summary / destination string, the method can return null.
8673    //
8674    // This string is used VERBATIM as the summary text of the relevant Settings item!
8675    public String getDestinationString(String transportName) {
8676        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8677                "getDestinationString");
8678
8679        synchronized (mTransports) {
8680            final IBackupTransport transport = mTransports.get(transportName);
8681            if (transport != null) {
8682                try {
8683                    final String text = transport.currentDestinationString();
8684                    if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
8685                    return text;
8686                } catch (RemoteException e) {
8687                    /* fall through to return null */
8688                }
8689            }
8690        }
8691
8692        return null;
8693    }
8694
8695    // Supply the manage-data intent for the given transport.
8696    public Intent getDataManagementIntent(String transportName) {
8697        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8698                "getDataManagementIntent");
8699
8700        synchronized (mTransports) {
8701            final IBackupTransport transport = mTransports.get(transportName);
8702            if (transport != null) {
8703                try {
8704                    final Intent intent = transport.dataManagementIntent();
8705                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
8706                            + intent);
8707                    return intent;
8708                } catch (RemoteException e) {
8709                    /* fall through to return null */
8710                }
8711            }
8712        }
8713
8714        return null;
8715    }
8716
8717    // Supply the menu label for affordances that fire the manage-data intent
8718    // for the given transport.
8719    public String getDataManagementLabel(String transportName) {
8720        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8721                "getDataManagementLabel");
8722
8723        synchronized (mTransports) {
8724            final IBackupTransport transport = mTransports.get(transportName);
8725            if (transport != null) {
8726                try {
8727                    final String text = transport.dataManagementLabel();
8728                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
8729                    return text;
8730                } catch (RemoteException e) {
8731                    /* fall through to return null */
8732                }
8733            }
8734        }
8735
8736        return null;
8737    }
8738
8739    // Callback: a requested backup agent has been instantiated.  This should only
8740    // be called from the Activity Manager.
8741    public void agentConnected(String packageName, IBinder agentBinder) {
8742        synchronized(mAgentConnectLock) {
8743            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
8744                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
8745                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
8746                mConnectedAgent = agent;
8747                mConnecting = false;
8748            } else {
8749                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
8750                        + " claiming agent connected");
8751            }
8752            mAgentConnectLock.notifyAll();
8753        }
8754    }
8755
8756    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
8757    // If the agent failed to come up in the first place, the agentBinder argument
8758    // will be null.  This should only be called from the Activity Manager.
8759    public void agentDisconnected(String packageName) {
8760        // TODO: handle backup being interrupted
8761        synchronized(mAgentConnectLock) {
8762            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
8763                mConnectedAgent = null;
8764                mConnecting = false;
8765            } else {
8766                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
8767                        + " claiming agent disconnected");
8768            }
8769            mAgentConnectLock.notifyAll();
8770        }
8771    }
8772
8773    // An application being installed will need a restore pass, then the Package Manager
8774    // will need to be told when the restore is finished.
8775    public void restoreAtInstall(String packageName, int token) {
8776        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
8777            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
8778                    + " attemping install-time restore");
8779            return;
8780        }
8781
8782        boolean skip = false;
8783
8784        long restoreSet = getAvailableRestoreToken(packageName);
8785        if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
8786                + " token=" + Integer.toHexString(token)
8787                + " restoreSet=" + Long.toHexString(restoreSet));
8788        if (restoreSet == 0) {
8789            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
8790            skip = true;
8791        }
8792
8793        // Do we have a transport to fetch data for us?
8794        IBackupTransport transport = getTransport(mCurrentTransport);
8795        if (transport == null) {
8796            if (DEBUG) Slog.w(TAG, "No transport");
8797            skip = true;
8798        }
8799
8800        if (!mAutoRestore) {
8801            if (DEBUG) {
8802                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
8803            }
8804            skip = true;
8805        }
8806
8807        if (!skip) {
8808            try {
8809                // okay, we're going to attempt a restore of this package from this restore set.
8810                // The eventual message back into the Package Manager to run the post-install
8811                // steps for 'token' will be issued from the restore handling code.
8812
8813                // This can throw and so *must* happen before the wakelock is acquired
8814                String dirName = transport.transportDirName();
8815
8816                // We can use a synthetic PackageInfo here because:
8817                //   1. We know it's valid, since the Package Manager supplied the name
8818                //   2. Only the packageName field will be used by the restore code
8819                PackageInfo pkg = new PackageInfo();
8820                pkg.packageName = packageName;
8821
8822                mWakelock.acquire();
8823                if (MORE_DEBUG) {
8824                    Slog.d(TAG, "Restore at install of " + packageName);
8825                }
8826                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
8827                msg.obj = new RestoreParams(transport, dirName, null,
8828                        restoreSet, pkg, token);
8829                mBackupHandler.sendMessage(msg);
8830            } catch (RemoteException e) {
8831                // Binding to the transport broke; back off and proceed with the installation.
8832                Slog.e(TAG, "Unable to contact transport");
8833                skip = true;
8834            }
8835        }
8836
8837        if (skip) {
8838            // Auto-restore disabled or no way to attempt a restore; just tell the Package
8839            // Manager to proceed with the post-install handling for this package.
8840            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
8841            try {
8842                mPackageManagerBinder.finishPackageInstall(token);
8843            } catch (RemoteException e) { /* can't happen */ }
8844        }
8845    }
8846
8847    // Hand off a restore session
8848    public IRestoreSession beginRestoreSession(String packageName, String transport) {
8849        if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
8850                + " transport=" + transport);
8851
8852        boolean needPermission = true;
8853        if (transport == null) {
8854            transport = mCurrentTransport;
8855
8856            if (packageName != null) {
8857                PackageInfo app = null;
8858                try {
8859                    app = mPackageManager.getPackageInfo(packageName, 0);
8860                } catch (NameNotFoundException nnf) {
8861                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
8862                    throw new IllegalArgumentException("Package " + packageName + " not found");
8863                }
8864
8865                if (app.applicationInfo.uid == Binder.getCallingUid()) {
8866                    // So: using the current active transport, and the caller has asked
8867                    // that its own package will be restored.  In this narrow use case
8868                    // we do not require the caller to hold the permission.
8869                    needPermission = false;
8870                }
8871            }
8872        }
8873
8874        if (needPermission) {
8875            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8876                    "beginRestoreSession");
8877        } else {
8878            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
8879        }
8880
8881        synchronized(this) {
8882            if (mActiveRestoreSession != null) {
8883                Slog.d(TAG, "Restore session requested but one already active");
8884                return null;
8885            }
8886            mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
8887            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
8888        }
8889        return mActiveRestoreSession;
8890    }
8891
8892    void clearRestoreSession(ActiveRestoreSession currentSession) {
8893        synchronized(this) {
8894            if (currentSession != mActiveRestoreSession) {
8895                Slog.e(TAG, "ending non-current restore session");
8896            } else {
8897                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
8898                mActiveRestoreSession = null;
8899                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
8900            }
8901        }
8902    }
8903
8904    // Note that a currently-active backup agent has notified us that it has
8905    // completed the given outstanding asynchronous backup/restore operation.
8906    public void opComplete(int token) {
8907        if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
8908        Operation op = null;
8909        synchronized (mCurrentOpLock) {
8910            op = mCurrentOperations.get(token);
8911            if (op != null) {
8912                op.state = OP_ACKNOWLEDGED;
8913            }
8914            mCurrentOpLock.notifyAll();
8915        }
8916
8917        // The completion callback, if any, is invoked on the handler
8918        if (op != null && op.callback != null) {
8919            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
8920            mBackupHandler.sendMessage(msg);
8921        }
8922    }
8923
8924    // ----- Restore session -----
8925
8926    class ActiveRestoreSession extends IRestoreSession.Stub {
8927        private static final String TAG = "RestoreSession";
8928
8929        private String mPackageName;
8930        private IBackupTransport mRestoreTransport = null;
8931        RestoreSet[] mRestoreSets = null;
8932        boolean mEnded = false;
8933        boolean mTimedOut = false;
8934
8935        ActiveRestoreSession(String packageName, String transport) {
8936            mPackageName = packageName;
8937            mRestoreTransport = getTransport(transport);
8938        }
8939
8940        public void markTimedOut() {
8941            mTimedOut = true;
8942        }
8943
8944        // --- Binder interface ---
8945        public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
8946            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8947                    "getAvailableRestoreSets");
8948            if (observer == null) {
8949                throw new IllegalArgumentException("Observer must not be null");
8950            }
8951
8952            if (mEnded) {
8953                throw new IllegalStateException("Restore session already ended");
8954            }
8955
8956            if (mTimedOut) {
8957                Slog.i(TAG, "Session already timed out");
8958                return -1;
8959            }
8960
8961            long oldId = Binder.clearCallingIdentity();
8962            try {
8963                if (mRestoreTransport == null) {
8964                    Slog.w(TAG, "Null transport getting restore sets");
8965                    return -1;
8966                }
8967
8968                // We know we're doing legit work now, so halt the timeout
8969                // until we're done.  It gets started again when the result
8970                // comes in.
8971                mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
8972
8973                // spin off the transport request to our service thread
8974                mWakelock.acquire();
8975                Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
8976                        new RestoreGetSetsParams(mRestoreTransport, this, observer));
8977                mBackupHandler.sendMessage(msg);
8978                return 0;
8979            } catch (Exception e) {
8980                Slog.e(TAG, "Error in getAvailableRestoreSets", e);
8981                return -1;
8982            } finally {
8983                Binder.restoreCallingIdentity(oldId);
8984            }
8985        }
8986
8987        public synchronized int restoreAll(long token, IRestoreObserver observer) {
8988            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8989                    "performRestore");
8990
8991            if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
8992                    + " observer=" + observer);
8993
8994            if (mEnded) {
8995                throw new IllegalStateException("Restore session already ended");
8996            }
8997
8998            if (mTimedOut) {
8999                Slog.i(TAG, "Session already timed out");
9000                return -1;
9001            }
9002
9003            if (mRestoreTransport == null || mRestoreSets == null) {
9004                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9005                return -1;
9006            }
9007
9008            if (mPackageName != null) {
9009                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9010                return -1;
9011            }
9012
9013            String dirName;
9014            try {
9015                dirName = mRestoreTransport.transportDirName();
9016            } catch (RemoteException e) {
9017                // Transport went AWOL; fail.
9018                Slog.e(TAG, "Unable to contact transport for restore");
9019                return -1;
9020            }
9021
9022            synchronized (mQueueLock) {
9023                for (int i = 0; i < mRestoreSets.length; i++) {
9024                    if (token == mRestoreSets[i].token) {
9025                        // Real work, so stop the session timeout until we finalize the restore
9026                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9027
9028                        long oldId = Binder.clearCallingIdentity();
9029                        mWakelock.acquire();
9030                        if (MORE_DEBUG) {
9031                            Slog.d(TAG, "restoreAll() kicking off");
9032                        }
9033                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9034                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
9035                                observer, token);
9036                        mBackupHandler.sendMessage(msg);
9037                        Binder.restoreCallingIdentity(oldId);
9038                        return 0;
9039                    }
9040                }
9041            }
9042
9043            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9044            return -1;
9045        }
9046
9047        // Restores of more than a single package are treated as 'system' restores
9048        public synchronized int restoreSome(long token, IRestoreObserver observer,
9049                String[] packages) {
9050            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9051                    "performRestore");
9052
9053            if (DEBUG) {
9054                StringBuilder b = new StringBuilder(128);
9055                b.append("restoreSome token=");
9056                b.append(Long.toHexString(token));
9057                b.append(" observer=");
9058                b.append(observer.toString());
9059                b.append(" packages=");
9060                if (packages == null) {
9061                    b.append("null");
9062                } else {
9063                    b.append('{');
9064                    boolean first = true;
9065                    for (String s : packages) {
9066                        if (!first) {
9067                            b.append(", ");
9068                        } else first = false;
9069                        b.append(s);
9070                    }
9071                    b.append('}');
9072                }
9073                Slog.d(TAG, b.toString());
9074            }
9075
9076            if (mEnded) {
9077                throw new IllegalStateException("Restore session already ended");
9078            }
9079
9080            if (mTimedOut) {
9081                Slog.i(TAG, "Session already timed out");
9082                return -1;
9083            }
9084
9085            if (mRestoreTransport == null || mRestoreSets == null) {
9086                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9087                return -1;
9088            }
9089
9090            if (mPackageName != null) {
9091                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9092                return -1;
9093            }
9094
9095            String dirName;
9096            try {
9097                dirName = mRestoreTransport.transportDirName();
9098            } catch (RemoteException e) {
9099                // Transport went AWOL; fail.
9100                Slog.e(TAG, "Unable to contact transport for restore");
9101                return -1;
9102            }
9103
9104            synchronized (mQueueLock) {
9105                for (int i = 0; i < mRestoreSets.length; i++) {
9106                    if (token == mRestoreSets[i].token) {
9107                        // Stop the session timeout until we finalize the restore
9108                        mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9109
9110                        long oldId = Binder.clearCallingIdentity();
9111                        mWakelock.acquire();
9112                        if (MORE_DEBUG) {
9113                            Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
9114                        }
9115                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9116                        msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
9117                                packages, packages.length > 1);
9118                        mBackupHandler.sendMessage(msg);
9119                        Binder.restoreCallingIdentity(oldId);
9120                        return 0;
9121                    }
9122                }
9123            }
9124
9125            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9126            return -1;
9127        }
9128
9129        public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
9130            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
9131
9132            if (mEnded) {
9133                throw new IllegalStateException("Restore session already ended");
9134            }
9135
9136            if (mTimedOut) {
9137                Slog.i(TAG, "Session already timed out");
9138                return -1;
9139            }
9140
9141            if (mPackageName != null) {
9142                if (! mPackageName.equals(packageName)) {
9143                    Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
9144                            + " on session for package " + mPackageName);
9145                    return -1;
9146                }
9147            }
9148
9149            PackageInfo app = null;
9150            try {
9151                app = mPackageManager.getPackageInfo(packageName, 0);
9152            } catch (NameNotFoundException nnf) {
9153                Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9154                return -1;
9155            }
9156
9157            // If the caller is not privileged and is not coming from the target
9158            // app's uid, throw a permission exception back to the caller.
9159            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
9160                    Binder.getCallingPid(), Binder.getCallingUid());
9161            if ((perm == PackageManager.PERMISSION_DENIED) &&
9162                    (app.applicationInfo.uid != Binder.getCallingUid())) {
9163                Slog.w(TAG, "restorePackage: bad packageName=" + packageName
9164                        + " or calling uid=" + Binder.getCallingUid());
9165                throw new SecurityException("No permission to restore other packages");
9166            }
9167
9168            // So far so good; we're allowed to try to restore this package.  Now
9169            // check whether there is data for it in the current dataset, falling back
9170            // to the ancestral dataset if not.
9171            long token = getAvailableRestoreToken(packageName);
9172
9173            // If we didn't come up with a place to look -- no ancestral dataset and
9174            // the app has never been backed up from this device -- there's nothing
9175            // to do but return failure.
9176            if (token == 0) {
9177                if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
9178                return -1;
9179            }
9180
9181            String dirName;
9182            try {
9183                dirName = mRestoreTransport.transportDirName();
9184            } catch (RemoteException e) {
9185                // Transport went AWOL; fail.
9186                Slog.e(TAG, "Unable to contact transport for restore");
9187                return -1;
9188            }
9189
9190            // Stop the session timeout until we finalize the restore
9191            mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9192
9193            // Ready to go:  enqueue the restore request and claim success
9194            long oldId = Binder.clearCallingIdentity();
9195            mWakelock.acquire();
9196            if (MORE_DEBUG) {
9197                Slog.d(TAG, "restorePackage() : " + packageName);
9198            }
9199            Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9200            msg.obj = new RestoreParams(mRestoreTransport, dirName,
9201                    observer, token, app, 0);
9202            mBackupHandler.sendMessage(msg);
9203            Binder.restoreCallingIdentity(oldId);
9204            return 0;
9205        }
9206
9207        // Posted to the handler to tear down a restore session in a cleanly synchronized way
9208        class EndRestoreRunnable implements Runnable {
9209            BackupManagerService mBackupManager;
9210            ActiveRestoreSession mSession;
9211
9212            EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
9213                mBackupManager = manager;
9214                mSession = session;
9215            }
9216
9217            public void run() {
9218                // clean up the session's bookkeeping
9219                synchronized (mSession) {
9220                    try {
9221                        if (mSession.mRestoreTransport != null) {
9222                            mSession.mRestoreTransport.finishRestore();
9223                        }
9224                    } catch (Exception e) {
9225                        Slog.e(TAG, "Error in finishRestore", e);
9226                    } finally {
9227                        mSession.mRestoreTransport = null;
9228                        mSession.mEnded = true;
9229                    }
9230                }
9231
9232                // clean up the BackupManagerImpl side of the bookkeeping
9233                // and cancel any pending timeout message
9234                mBackupManager.clearRestoreSession(mSession);
9235            }
9236        }
9237
9238        public synchronized void endRestoreSession() {
9239            if (DEBUG) Slog.d(TAG, "endRestoreSession");
9240
9241            if (mTimedOut) {
9242                Slog.i(TAG, "Session already timed out");
9243                return;
9244            }
9245
9246            if (mEnded) {
9247                throw new IllegalStateException("Restore session already ended");
9248            }
9249
9250            mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
9251        }
9252    }
9253
9254    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
9255        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
9256
9257        long identityToken = Binder.clearCallingIdentity();
9258        try {
9259            if (args != null) {
9260                for (String arg : args) {
9261                    if ("-h".equals(arg)) {
9262                        pw.println("'dumpsys backup' optional arguments:");
9263                        pw.println("  -h       : this help text");
9264                        pw.println("  a[gents] : dump information about defined backup agents");
9265                        return;
9266                    } else if ("agents".startsWith(arg)) {
9267                        dumpAgents(pw);
9268                        return;
9269                    }
9270                }
9271            }
9272            dumpInternal(pw);
9273        } finally {
9274            Binder.restoreCallingIdentity(identityToken);
9275        }
9276    }
9277
9278    private void dumpAgents(PrintWriter pw) {
9279        List<PackageInfo> agentPackages = allAgentPackages();
9280        pw.println("Defined backup agents:");
9281        for (PackageInfo pkg : agentPackages) {
9282            pw.print("  ");
9283            pw.print(pkg.packageName); pw.println(':');
9284            pw.print("      "); pw.println(pkg.applicationInfo.backupAgentName);
9285        }
9286    }
9287
9288    private void dumpInternal(PrintWriter pw) {
9289        synchronized (mQueueLock) {
9290            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
9291                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
9292                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
9293            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
9294            if (mBackupRunning) pw.println("Backup currently running");
9295            pw.println("Last backup pass started: " + mLastBackupPass
9296                    + " (now = " + System.currentTimeMillis() + ')');
9297            pw.println("  next scheduled: " + mNextBackupPass);
9298
9299            pw.println("Available transports:");
9300            final String[] transports = listAllTransports();
9301            if (transports != null) {
9302                for (String t : listAllTransports()) {
9303                    pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
9304                    try {
9305                        IBackupTransport transport = getTransport(t);
9306                        File dir = new File(mBaseStateDir, transport.transportDirName());
9307                        pw.println("       destination: " + transport.currentDestinationString());
9308                        pw.println("       intent: " + transport.configurationIntent());
9309                        for (File f : dir.listFiles()) {
9310                            pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
9311                        }
9312                    } catch (Exception e) {
9313                        Slog.e(TAG, "Error in transport", e);
9314                        pw.println("        Error: " + e);
9315                    }
9316                }
9317            }
9318
9319            pw.println("Pending init: " + mPendingInits.size());
9320            for (String s : mPendingInits) {
9321                pw.println("    " + s);
9322            }
9323
9324            if (DEBUG_BACKUP_TRACE) {
9325                synchronized (mBackupTrace) {
9326                    if (!mBackupTrace.isEmpty()) {
9327                        pw.println("Most recent backup trace:");
9328                        for (String s : mBackupTrace) {
9329                            pw.println("   " + s);
9330                        }
9331                    }
9332                }
9333            }
9334
9335            int N = mBackupParticipants.size();
9336            pw.println("Participants:");
9337            for (int i=0; i<N; i++) {
9338                int uid = mBackupParticipants.keyAt(i);
9339                pw.print("  uid: ");
9340                pw.println(uid);
9341                HashSet<String> participants = mBackupParticipants.valueAt(i);
9342                for (String app: participants) {
9343                    pw.println("    " + app);
9344                }
9345            }
9346
9347            pw.println("Ancestral packages: "
9348                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
9349            if (mAncestralPackages != null) {
9350                for (String pkg : mAncestralPackages) {
9351                    pw.println("    " + pkg);
9352                }
9353            }
9354
9355            pw.println("Ever backed up: " + mEverStoredApps.size());
9356            for (String pkg : mEverStoredApps) {
9357                pw.println("    " + pkg);
9358            }
9359
9360            pw.println("Pending key/value backup: " + mPendingBackups.size());
9361            for (BackupRequest req : mPendingBackups.values()) {
9362                pw.println("    " + req);
9363            }
9364
9365            pw.println("Full backup queue:" + mFullBackupQueue.size());
9366            for (FullBackupEntry entry : mFullBackupQueue) {
9367                pw.print("    "); pw.print(entry.lastBackup);
9368                pw.print(" : "); pw.println(entry.packageName);
9369            }
9370        }
9371    }
9372}
9373