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