BackupManagerService.java revision 5923c9718390bf6d50c52661263f15c1f863012b
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.app.ActivityManagerNative;
20import android.app.ActivityThread;
21import android.app.AlarmManager;
22import android.app.IActivityManager;
23import android.app.IApplicationThread;
24import android.app.IBackupAgent;
25import android.app.PendingIntent;
26import android.app.backup.RestoreSet;
27import android.app.backup.IBackupManager;
28import android.app.backup.IRestoreObserver;
29import android.app.backup.IRestoreSession;
30import android.content.BroadcastReceiver;
31import android.content.ComponentName;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.ServiceConnection;
36import android.content.pm.ApplicationInfo;
37import android.content.pm.IPackageDataObserver;
38import android.content.pm.IPackageManager;
39import android.content.pm.PackageInfo;
40import android.content.pm.PackageManager;
41import android.content.pm.Signature;
42import android.content.pm.PackageManager.NameNotFoundException;
43import android.net.Uri;
44import android.os.Binder;
45import android.os.Bundle;
46import android.os.Environment;
47import android.os.Handler;
48import android.os.HandlerThread;
49import android.os.IBinder;
50import android.os.Looper;
51import android.os.Message;
52import android.os.ParcelFileDescriptor;
53import android.os.PowerManager;
54import android.os.Process;
55import android.os.RemoteException;
56import android.os.SystemClock;
57import android.provider.Settings;
58import android.util.EventLog;
59import android.util.Slog;
60import android.util.SparseArray;
61import android.util.SparseIntArray;
62
63import com.android.internal.backup.BackupConstants;
64import com.android.internal.backup.IBackupTransport;
65import com.android.internal.backup.LocalTransport;
66import com.android.server.PackageManagerBackupAgent.Metadata;
67
68import java.io.EOFException;
69import java.io.File;
70import java.io.FileDescriptor;
71import java.io.FileNotFoundException;
72import java.io.FileOutputStream;
73import java.io.IOException;
74import java.io.PrintWriter;
75import java.io.RandomAccessFile;
76import java.util.ArrayList;
77import java.util.HashMap;
78import java.util.HashSet;
79import java.util.List;
80import java.util.Map;
81import java.util.Random;
82import java.util.Set;
83
84class BackupManagerService extends IBackupManager.Stub {
85    private static final String TAG = "BackupManagerService";
86    private static final boolean DEBUG = false;
87
88    // How often we perform a backup pass.  Privileged external callers can
89    // trigger an immediate pass.
90    private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
91
92    // Random variation in backup scheduling time to avoid server load spikes
93    private static final int FUZZ_MILLIS = 5 * 60 * 1000;
94
95    // The amount of time between the initial provisioning of the device and
96    // the first backup pass.
97    private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
98
99    private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
100    private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
101    private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
102    private static final int MSG_RUN_BACKUP = 1;
103    private static final int MSG_RUN_FULL_BACKUP = 2;
104    private static final int MSG_RUN_RESTORE = 3;
105    private static final int MSG_RUN_CLEAR = 4;
106    private static final int MSG_RUN_INITIALIZE = 5;
107    private static final int MSG_RUN_GET_RESTORE_SETS = 6;
108    private static final int MSG_TIMEOUT = 7;
109
110    // Timeout interval for deciding that a bind or clear-data has taken too long
111    static final long TIMEOUT_INTERVAL = 10 * 1000;
112
113    // Timeout intervals for agent backup & restore operations
114    static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
115    static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
116
117    private Context mContext;
118    private PackageManager mPackageManager;
119    IPackageManager mPackageManagerBinder;
120    private IActivityManager mActivityManager;
121    private PowerManager mPowerManager;
122    private AlarmManager mAlarmManager;
123    IBackupManager mBackupManagerBinder;
124
125    boolean mEnabled;   // access to this is synchronized on 'this'
126    boolean mProvisioned;
127    boolean mAutoRestore;
128    PowerManager.WakeLock mWakelock;
129    HandlerThread mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
130    BackupHandler mBackupHandler;
131    PendingIntent mRunBackupIntent, mRunInitIntent;
132    BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
133    // map UIDs to the set of backup client services within that UID's app set
134    final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
135        = new SparseArray<HashSet<ApplicationInfo>>();
136    // set of backup services that have pending changes
137    class BackupRequest {
138        public ApplicationInfo appInfo;
139        public boolean fullBackup;
140
141        BackupRequest(ApplicationInfo app, boolean isFull) {
142            appInfo = app;
143            fullBackup = isFull;
144        }
145
146        public String toString() {
147            return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
148        }
149    }
150    // Backups that we haven't started yet.
151    HashMap<ApplicationInfo,BackupRequest> mPendingBackups
152            = new HashMap<ApplicationInfo,BackupRequest>();
153
154    // Pseudoname that we use for the Package Manager metadata "package"
155    static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
156
157    // locking around the pending-backup management
158    final Object mQueueLock = new Object();
159
160    // The thread performing the sequence of queued backups binds to each app's agent
161    // in succession.  Bind notifications are asynchronously delivered through the
162    // Activity Manager; use this lock object to signal when a requested binding has
163    // completed.
164    final Object mAgentConnectLock = new Object();
165    IBackupAgent mConnectedAgent;
166    volatile boolean mConnecting;
167    volatile long mLastBackupPass;
168    volatile long mNextBackupPass;
169
170    // A similar synchronization mechanism around clearing apps' data for restore
171    final Object mClearDataLock = new Object();
172    volatile boolean mClearingData;
173
174    // Transport bookkeeping
175    final HashMap<String,IBackupTransport> mTransports
176            = new HashMap<String,IBackupTransport>();
177    String mCurrentTransport;
178    IBackupTransport mLocalTransport, mGoogleTransport;
179    ActiveRestoreSession mActiveRestoreSession;
180
181    class RestoreGetSetsParams {
182        public IBackupTransport transport;
183        public ActiveRestoreSession session;
184        public IRestoreObserver observer;
185
186        RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
187                IRestoreObserver _observer) {
188            transport = _transport;
189            session = _session;
190            observer = _observer;
191        }
192    }
193
194    class RestoreParams {
195        public IBackupTransport transport;
196        public IRestoreObserver observer;
197        public long token;
198        public PackageInfo pkgInfo;
199        public int pmToken; // in post-install restore, the PM's token for this transaction
200
201        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
202                long _token, PackageInfo _pkg, int _pmToken) {
203            transport = _transport;
204            observer = _obs;
205            token = _token;
206            pkgInfo = _pkg;
207            pmToken = _pmToken;
208        }
209
210        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
211            transport = _transport;
212            observer = _obs;
213            token = _token;
214            pkgInfo = null;
215            pmToken = 0;
216        }
217    }
218
219    class ClearParams {
220        public IBackupTransport transport;
221        public PackageInfo packageInfo;
222
223        ClearParams(IBackupTransport _transport, PackageInfo _info) {
224            transport = _transport;
225            packageInfo = _info;
226        }
227    }
228
229    // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
230    // token is the index of the entry in the pending-operations list.
231    static final int OP_PENDING = 0;
232    static final int OP_ACKNOWLEDGED = 1;
233    static final int OP_TIMEOUT = -1;
234
235    final SparseIntArray mCurrentOperations = new SparseIntArray();
236    final Object mCurrentOpLock = new Object();
237    final Random mTokenGenerator = new Random();
238
239    // Where we keep our journal files and other bookkeeping
240    File mBaseStateDir;
241    File mDataDir;
242    File mJournalDir;
243    File mJournal;
244
245    // Keep a log of all the apps we've ever backed up, and what the
246    // dataset tokens are for both the current backup dataset and
247    // the ancestral dataset.
248    private File mEverStored;
249    HashSet<String> mEverStoredApps = new HashSet<String>();
250
251    static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;  // increment when the schema changes
252    File mTokenFile;
253    Set<String> mAncestralPackages = null;
254    long mAncestralToken = 0;
255    long mCurrentToken = 0;
256
257    // Persistently track the need to do a full init
258    static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
259    HashSet<String> mPendingInits = new HashSet<String>();  // transport names
260
261    // ----- Asynchronous backup/restore handler thread -----
262
263    private class BackupHandler extends Handler {
264        public BackupHandler(Looper looper) {
265            super(looper);
266        }
267
268        public void handleMessage(Message msg) {
269
270            switch (msg.what) {
271            case MSG_RUN_BACKUP:
272            {
273                mLastBackupPass = System.currentTimeMillis();
274                mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
275
276                IBackupTransport transport = getTransport(mCurrentTransport);
277                if (transport == null) {
278                    Slog.v(TAG, "Backup requested but no transport available");
279                    mWakelock.release();
280                    break;
281                }
282
283                // snapshot the pending-backup set and work on that
284                ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
285                File oldJournal = mJournal;
286                synchronized (mQueueLock) {
287                    // Do we have any work to do?  Construct the work queue
288                    // then release the synchronization lock to actually run
289                    // the backup.
290                    if (mPendingBackups.size() > 0) {
291                        for (BackupRequest b: mPendingBackups.values()) {
292                            queue.add(b);
293                        }
294                        if (DEBUG) Slog.v(TAG, "clearing pending backups");
295                        mPendingBackups.clear();
296
297                        // Start a new backup-queue journal file too
298                        mJournal = null;
299
300                    }
301                }
302
303                if (queue.size() > 0) {
304                    // At this point, we have started a new journal file, and the old
305                    // file identity is being passed to the backup processing thread.
306                    // When it completes successfully, that old journal file will be
307                    // deleted.  If we crash prior to that, the old journal is parsed
308                    // at next boot and the journaled requests fulfilled.
309                    (new PerformBackupTask(transport, queue, oldJournal)).run();
310                } else {
311                    Slog.v(TAG, "Backup requested but nothing pending");
312                    mWakelock.release();
313                }
314                break;
315            }
316
317            case MSG_RUN_FULL_BACKUP:
318                break;
319
320            case MSG_RUN_RESTORE:
321            {
322                RestoreParams params = (RestoreParams)msg.obj;
323                Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
324                (new PerformRestoreTask(params.transport, params.observer,
325                        params.token, params.pkgInfo, params.pmToken)).run();
326                break;
327            }
328
329            case MSG_RUN_CLEAR:
330            {
331                ClearParams params = (ClearParams)msg.obj;
332                (new PerformClearTask(params.transport, params.packageInfo)).run();
333                break;
334            }
335
336            case MSG_RUN_INITIALIZE:
337            {
338                HashSet<String> queue;
339
340                // Snapshot the pending-init queue and work on that
341                synchronized (mQueueLock) {
342                    queue = new HashSet<String>(mPendingInits);
343                    mPendingInits.clear();
344                }
345
346                (new PerformInitializeTask(queue)).run();
347                break;
348            }
349
350            case MSG_RUN_GET_RESTORE_SETS:
351            {
352                // Like other async operations, this is entered with the wakelock held
353                RestoreSet[] sets = null;
354                RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
355                try {
356                    sets = params.transport.getAvailableRestoreSets();
357                    // cache the result in the active session
358                    synchronized (params.session) {
359                        params.session.mRestoreSets = sets;
360                    }
361                    if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
362                } catch (Exception e) {
363                    Slog.e(TAG, "Error from transport getting set list");
364                } finally {
365                    if (params.observer != null) {
366                        try {
367                            params.observer.restoreSetsAvailable(sets);
368                        } catch (RemoteException re) {
369                            Slog.e(TAG, "Unable to report listing to observer");
370                        } catch (Exception e) {
371                            Slog.e(TAG, "Restore observer threw", e);
372                        }
373                    }
374
375                    mWakelock.release();
376                }
377                break;
378            }
379
380            case MSG_TIMEOUT:
381            {
382                synchronized (mCurrentOpLock) {
383                    final int token = msg.arg1;
384                    int state = mCurrentOperations.get(token, OP_TIMEOUT);
385                    if (state == OP_PENDING) {
386                        if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + token);
387                        mCurrentOperations.put(token, OP_TIMEOUT);
388                    }
389                    mCurrentOpLock.notifyAll();
390                }
391                break;
392            }
393            }
394        }
395    }
396
397    // ----- Main service implementation -----
398
399    public BackupManagerService(Context context) {
400        mContext = context;
401        mPackageManager = context.getPackageManager();
402        mPackageManagerBinder = ActivityThread.getPackageManager();
403        mActivityManager = ActivityManagerNative.getDefault();
404
405        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
406        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
407
408        mBackupManagerBinder = asInterface(asBinder());
409
410        // spin up the backup/restore handler thread
411        mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
412        mHandlerThread.start();
413        mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
414
415        // Set up our bookkeeping
416        boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
417                Settings.Secure.BACKUP_ENABLED, 0) != 0;
418        mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
419                Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
420        mAutoRestore = Settings.Secure.getInt(context.getContentResolver(),
421                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
422        // If Encrypted file systems is enabled or disabled, this call will return the
423        // correct directory.
424        mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
425        mBaseStateDir.mkdirs();
426        mDataDir = Environment.getDownloadCacheDirectory();
427
428        // Alarm receivers for scheduled backups & initialization operations
429        mRunBackupReceiver = new RunBackupReceiver();
430        IntentFilter filter = new IntentFilter();
431        filter.addAction(RUN_BACKUP_ACTION);
432        context.registerReceiver(mRunBackupReceiver, filter,
433                android.Manifest.permission.BACKUP, null);
434
435        mRunInitReceiver = new RunInitializeReceiver();
436        filter = new IntentFilter();
437        filter.addAction(RUN_INITIALIZE_ACTION);
438        context.registerReceiver(mRunInitReceiver, filter,
439                android.Manifest.permission.BACKUP, null);
440
441        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
442        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
443        mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
444
445        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
446        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
447        mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
448
449        // Set up the backup-request journaling
450        mJournalDir = new File(mBaseStateDir, "pending");
451        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
452        mJournal = null;        // will be created on first use
453
454        // Set up the various sorts of package tracking we do
455        initPackageTracking();
456
457        // Build our mapping of uid to backup client services.  This implicitly
458        // schedules a backup pass on the Package Manager metadata the first
459        // time anything needs to be backed up.
460        synchronized (mBackupParticipants) {
461            addPackageParticipantsLocked(null);
462        }
463
464        // Set up our transport options and initialize the default transport
465        // TODO: Have transports register themselves somehow?
466        // TODO: Don't create transports that we don't need to?
467        mLocalTransport = new LocalTransport(context);  // This is actually pretty cheap
468        ComponentName localName = new ComponentName(context, LocalTransport.class);
469        registerTransport(localName.flattenToShortString(), mLocalTransport);
470
471        mGoogleTransport = null;
472        mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
473                Settings.Secure.BACKUP_TRANSPORT);
474        if ("".equals(mCurrentTransport)) {
475            mCurrentTransport = null;
476        }
477        if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
478
479        // Attach to the Google backup transport.  When this comes up, it will set
480        // itself as the current transport because we explicitly reset mCurrentTransport
481        // to null.
482        Intent intent = new Intent().setComponent(new ComponentName(
483                "com.google.android.backup",
484                "com.google.android.backup.BackupTransportService"));
485        context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
486
487        // Now that we know about valid backup participants, parse any
488        // leftover journal files into the pending backup set
489        parseLeftoverJournals();
490
491        // Power management
492        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
493
494        // Start the backup passes going
495        setBackupEnabled(areEnabled);
496    }
497
498    private class RunBackupReceiver extends BroadcastReceiver {
499        public void onReceive(Context context, Intent intent) {
500            if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
501                synchronized (mQueueLock) {
502                    if (mPendingInits.size() > 0) {
503                        // If there are pending init operations, we process those
504                        // and then settle into the usual periodic backup schedule.
505                        if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
506                        try {
507                            mAlarmManager.cancel(mRunInitIntent);
508                            mRunInitIntent.send();
509                        } catch (PendingIntent.CanceledException ce) {
510                            Slog.e(TAG, "Run init intent cancelled");
511                            // can't really do more than bail here
512                        }
513                    } else {
514                        // Don't run backups now if we're disabled or not yet
515                        // fully set up.
516                        if (mEnabled && mProvisioned) {
517                            if (DEBUG) Slog.v(TAG, "Running a backup pass");
518
519                            // Acquire the wakelock and pass it to the backup thread.  it will
520                            // be released once backup concludes.
521                            mWakelock.acquire();
522
523                            Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
524                            mBackupHandler.sendMessage(msg);
525                        } else {
526                            Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
527                        }
528                    }
529                }
530            }
531        }
532    }
533
534    private class RunInitializeReceiver extends BroadcastReceiver {
535        public void onReceive(Context context, Intent intent) {
536            if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
537                synchronized (mQueueLock) {
538                    if (DEBUG) Slog.v(TAG, "Running a device init");
539
540                    // Acquire the wakelock and pass it to the init thread.  it will
541                    // be released once init concludes.
542                    mWakelock.acquire();
543
544                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
545                    mBackupHandler.sendMessage(msg);
546                }
547            }
548        }
549    }
550
551    private void initPackageTracking() {
552        if (DEBUG) Slog.v(TAG, "Initializing package tracking");
553
554        // Remember our ancestral dataset
555        mTokenFile = new File(mBaseStateDir, "ancestral");
556        try {
557            RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
558            int version = tf.readInt();
559            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
560                mAncestralToken = tf.readLong();
561                mCurrentToken = tf.readLong();
562
563                int numPackages = tf.readInt();
564                if (numPackages >= 0) {
565                    mAncestralPackages = new HashSet<String>();
566                    for (int i = 0; i < numPackages; i++) {
567                        String pkgName = tf.readUTF();
568                        mAncestralPackages.add(pkgName);
569                    }
570                }
571            }
572        } catch (FileNotFoundException fnf) {
573            // Probably innocuous
574            Slog.v(TAG, "No ancestral data");
575        } catch (IOException e) {
576            Slog.w(TAG, "Unable to read token file", e);
577        }
578
579        // Keep a log of what apps we've ever backed up.  Because we might have
580        // rebooted in the middle of an operation that was removing something from
581        // this log, we sanity-check its contents here and reconstruct it.
582        mEverStored = new File(mBaseStateDir, "processed");
583        File tempProcessedFile = new File(mBaseStateDir, "processed.new");
584
585        // If we were in the middle of removing something from the ever-backed-up
586        // file, there might be a transient "processed.new" file still present.
587        // Ignore it -- we'll validate "processed" against the current package set.
588        if (tempProcessedFile.exists()) {
589            tempProcessedFile.delete();
590        }
591
592        // If there are previous contents, parse them out then start a new
593        // file to continue the recordkeeping.
594        if (mEverStored.exists()) {
595            RandomAccessFile temp = null;
596            RandomAccessFile in = null;
597
598            try {
599                temp = new RandomAccessFile(tempProcessedFile, "rws");
600                in = new RandomAccessFile(mEverStored, "r");
601
602                while (true) {
603                    PackageInfo info;
604                    String pkg = in.readUTF();
605                    try {
606                        info = mPackageManager.getPackageInfo(pkg, 0);
607                        mEverStoredApps.add(pkg);
608                        temp.writeUTF(pkg);
609                        if (DEBUG) Slog.v(TAG, "   + " + pkg);
610                    } catch (NameNotFoundException e) {
611                        // nope, this package was uninstalled; don't include it
612                        if (DEBUG) Slog.v(TAG, "   - " + pkg);
613                    }
614                }
615            } catch (EOFException e) {
616                // Once we've rewritten the backup history log, atomically replace the
617                // old one with the new one then reopen the file for continuing use.
618                if (!tempProcessedFile.renameTo(mEverStored)) {
619                    Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
620                }
621            } catch (IOException e) {
622                Slog.e(TAG, "Error in processed file", e);
623            } finally {
624                try { if (temp != null) temp.close(); } catch (IOException e) {}
625                try { if (in != null) in.close(); } catch (IOException e) {}
626            }
627        }
628
629        // Register for broadcasts about package install, etc., so we can
630        // update the provider list.
631        IntentFilter filter = new IntentFilter();
632        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
633        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
634        filter.addDataScheme("package");
635        mContext.registerReceiver(mBroadcastReceiver, filter);
636        // Register for events related to sdcard installation.
637        IntentFilter sdFilter = new IntentFilter();
638        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
639        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
640        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
641    }
642
643    private void parseLeftoverJournals() {
644        for (File f : mJournalDir.listFiles()) {
645            if (mJournal == null || f.compareTo(mJournal) != 0) {
646                // This isn't the current journal, so it must be a leftover.  Read
647                // out the package names mentioned there and schedule them for
648                // backup.
649                RandomAccessFile in = null;
650                try {
651                    Slog.i(TAG, "Found stale backup journal, scheduling:");
652                    in = new RandomAccessFile(f, "r");
653                    while (true) {
654                        String packageName = in.readUTF();
655                        Slog.i(TAG, "    + " + packageName);
656                        dataChanged(packageName);
657                    }
658                } catch (EOFException e) {
659                    // no more data; we're done
660                } catch (Exception e) {
661                    Slog.e(TAG, "Can't read " + f, e);
662                } finally {
663                    // close/delete the file
664                    try { if (in != null) in.close(); } catch (IOException e) {}
665                    f.delete();
666                }
667            }
668        }
669    }
670
671    // Maintain persistent state around whether need to do an initialize operation.
672    // Must be called with the queue lock held.
673    void recordInitPendingLocked(boolean isPending, String transportName) {
674        if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
675                + " on transport " + transportName);
676        try {
677            IBackupTransport transport = getTransport(transportName);
678            String transportDirName = transport.transportDirName();
679            File stateDir = new File(mBaseStateDir, transportDirName);
680            File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
681
682            if (isPending) {
683                // We need an init before we can proceed with sending backup data.
684                // Record that with an entry in our set of pending inits, as well as
685                // journaling it via creation of a sentinel file.
686                mPendingInits.add(transportName);
687                try {
688                    (new FileOutputStream(initPendingFile)).close();
689                } catch (IOException ioe) {
690                    // Something is badly wrong with our permissions; just try to move on
691                }
692            } else {
693                // No more initialization needed; wipe the journal and reset our state.
694                initPendingFile.delete();
695                mPendingInits.remove(transportName);
696            }
697        } catch (RemoteException e) {
698            // can't happen; the transport is local
699        }
700    }
701
702    // Reset all of our bookkeeping, in response to having been told that
703    // the backend data has been wiped [due to idle expiry, for example],
704    // so we must re-upload all saved settings.
705    void resetBackupState(File stateFileDir) {
706        synchronized (mQueueLock) {
707            // Wipe the "what we've ever backed up" tracking
708            mEverStoredApps.clear();
709            mEverStored.delete();
710
711            mCurrentToken = 0;
712            writeRestoreTokens();
713
714            // Remove all the state files
715            for (File sf : stateFileDir.listFiles()) {
716                // ... but don't touch the needs-init sentinel
717                if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
718                    sf.delete();
719                }
720            }
721
722            // Enqueue a new backup of every participant
723            int N = mBackupParticipants.size();
724            for (int i=0; i<N; i++) {
725                int uid = mBackupParticipants.keyAt(i);
726                HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
727                for (ApplicationInfo app: participants) {
728                    dataChanged(app.packageName);
729                }
730            }
731        }
732    }
733
734    // Add a transport to our set of available backends.  If 'transport' is null, this
735    // is an unregistration, and the transport's entry is removed from our bookkeeping.
736    private void registerTransport(String name, IBackupTransport transport) {
737        synchronized (mTransports) {
738            if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport);
739            if (transport != null) {
740                mTransports.put(name, transport);
741            } else {
742                mTransports.remove(name);
743                if ((mCurrentTransport != null) && mCurrentTransport.equals(name)) {
744                    mCurrentTransport = null;
745                }
746                // Nothing further to do in the unregistration case
747                return;
748            }
749        }
750
751        // If the init sentinel file exists, we need to be sure to perform the init
752        // as soon as practical.  We also create the state directory at registration
753        // time to ensure it's present from the outset.
754        try {
755            String transportName = transport.transportDirName();
756            File stateDir = new File(mBaseStateDir, transportName);
757            stateDir.mkdirs();
758
759            File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
760            if (initSentinel.exists()) {
761                synchronized (mQueueLock) {
762                    mPendingInits.add(transportName);
763
764                    // TODO: pick a better starting time than now + 1 minute
765                    long delay = 1000 * 60; // one minute, in milliseconds
766                    mAlarmManager.set(AlarmManager.RTC_WAKEUP,
767                            System.currentTimeMillis() + delay, mRunInitIntent);
768                }
769            }
770        } catch (RemoteException e) {
771            // can't happen, the transport is local
772        }
773    }
774
775    // ----- Track installation/removal of packages -----
776    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
777        public void onReceive(Context context, Intent intent) {
778            if (DEBUG) Slog.d(TAG, "Received broadcast " + intent);
779
780            String action = intent.getAction();
781            boolean replacing = false;
782            boolean added = false;
783            Bundle extras = intent.getExtras();
784            String pkgList[] = null;
785            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
786                    Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
787                Uri uri = intent.getData();
788                if (uri == null) {
789                    return;
790                }
791                String pkgName = uri.getSchemeSpecificPart();
792                if (pkgName != null) {
793                    pkgList = new String[] { pkgName };
794                }
795                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
796                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
797            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
798                added = true;
799                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
800            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
801                added = false;
802                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
803            }
804            if (pkgList == null || pkgList.length == 0) {
805                return;
806            }
807            if (added) {
808                synchronized (mBackupParticipants) {
809                    for (String pkgName : pkgList) {
810                        if (replacing) {
811                            // The package was just upgraded
812                            updatePackageParticipantsLocked(pkgName);
813                        } else {
814                            // The package was just added
815                            addPackageParticipantsLocked(pkgName);
816                        }
817                    }
818                }
819            } else {
820                if (replacing) {
821                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
822                } else {
823                    synchronized (mBackupParticipants) {
824                        for (String pkgName : pkgList) {
825                            removePackageParticipantsLocked(pkgName);
826                        }
827                    }
828                }
829            }
830        }
831    };
832
833    // ----- Track connection to GoogleBackupTransport service -----
834    ServiceConnection mGoogleConnection = new ServiceConnection() {
835        public void onServiceConnected(ComponentName name, IBinder service) {
836            if (DEBUG) Slog.v(TAG, "Connected to Google transport");
837            mGoogleTransport = IBackupTransport.Stub.asInterface(service);
838            registerTransport(name.flattenToShortString(), mGoogleTransport);
839        }
840
841        public void onServiceDisconnected(ComponentName name) {
842            if (DEBUG) Slog.v(TAG, "Disconnected from Google transport");
843            mGoogleTransport = null;
844            registerTransport(name.flattenToShortString(), null);
845        }
846    };
847
848    // Add the backup agents in the given package to our set of known backup participants.
849    // If 'packageName' is null, adds all backup agents in the whole system.
850    void addPackageParticipantsLocked(String packageName) {
851        // Look for apps that define the android:backupAgent attribute
852        if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: " + packageName);
853        List<PackageInfo> targetApps = allAgentPackages();
854        addPackageParticipantsLockedInner(packageName, targetApps);
855    }
856
857    private void addPackageParticipantsLockedInner(String packageName,
858            List<PackageInfo> targetPkgs) {
859        if (DEBUG) {
860            Slog.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
861            for (PackageInfo p : targetPkgs) {
862                Slog.v(TAG, "    " + p + " agent=" + p.applicationInfo.backupAgentName
863                        + " uid=" + p.applicationInfo.uid
864                        + " killAfterRestore="
865                        + (((p.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) ? "true" : "false")
866                        );
867            }
868        }
869
870        for (PackageInfo pkg : targetPkgs) {
871            if (packageName == null || pkg.packageName.equals(packageName)) {
872                int uid = pkg.applicationInfo.uid;
873                HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
874                if (set == null) {
875                    set = new HashSet<ApplicationInfo>();
876                    mBackupParticipants.put(uid, set);
877                }
878                set.add(pkg.applicationInfo);
879
880                // If we've never seen this app before, schedule a backup for it
881                if (!mEverStoredApps.contains(pkg.packageName)) {
882                    if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName
883                            + " never backed up; scheduling");
884                    dataChanged(pkg.packageName);
885                }
886            }
887        }
888    }
889
890    // Remove the given package's entry from our known active set.  If
891    // 'packageName' is null, *all* participating apps will be removed.
892    void removePackageParticipantsLocked(String packageName) {
893        if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: " + packageName);
894        List<PackageInfo> allApps = null;
895        if (packageName != null) {
896            allApps = new ArrayList<PackageInfo>();
897            try {
898                int flags = PackageManager.GET_SIGNATURES;
899                allApps.add(mPackageManager.getPackageInfo(packageName, flags));
900            } catch (Exception e) {
901                // just skip it (???)
902            }
903        } else {
904            // all apps with agents
905            allApps = allAgentPackages();
906        }
907        removePackageParticipantsLockedInner(packageName, allApps);
908    }
909
910    private void removePackageParticipantsLockedInner(String packageName,
911            List<PackageInfo> agents) {
912        if (DEBUG) {
913            Slog.v(TAG, "removePackageParticipantsLockedInner (" + packageName
914                    + ") removing " + agents.size() + " entries");
915            for (PackageInfo p : agents) {
916                Slog.v(TAG, "    - " + p);
917            }
918        }
919        for (PackageInfo pkg : agents) {
920            if (packageName == null || pkg.packageName.equals(packageName)) {
921                int uid = pkg.applicationInfo.uid;
922                HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
923                if (set != null) {
924                    // Find the existing entry with the same package name, and remove it.
925                    // We can't just remove(app) because the instances are different.
926                    for (ApplicationInfo entry: set) {
927                        if (entry.packageName.equals(pkg.packageName)) {
928                            set.remove(entry);
929                            removeEverBackedUp(pkg.packageName);
930                            break;
931                        }
932                    }
933                    if (set.size() == 0) {
934                        mBackupParticipants.delete(uid);
935                    }
936                }
937            }
938        }
939    }
940
941    // Returns the set of all applications that define an android:backupAgent attribute
942    List<PackageInfo> allAgentPackages() {
943        // !!! TODO: cache this and regenerate only when necessary
944        int flags = PackageManager.GET_SIGNATURES;
945        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
946        int N = packages.size();
947        for (int a = N-1; a >= 0; a--) {
948            PackageInfo pkg = packages.get(a);
949            try {
950                ApplicationInfo app = pkg.applicationInfo;
951                if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
952                        || app.backupAgentName == null) {
953                    packages.remove(a);
954                }
955                else {
956                    // we will need the shared library path, so look that up and store it here
957                    app = mPackageManager.getApplicationInfo(pkg.packageName,
958                            PackageManager.GET_SHARED_LIBRARY_FILES);
959                    pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
960                }
961            } catch (NameNotFoundException e) {
962                packages.remove(a);
963            }
964        }
965        return packages;
966    }
967
968    // Reset the given package's known backup participants.  Unlike add/remove, the update
969    // action cannot be passed a null package name.
970    void updatePackageParticipantsLocked(String packageName) {
971        if (packageName == null) {
972            Slog.e(TAG, "updatePackageParticipants called with null package name");
973            return;
974        }
975        if (DEBUG) Slog.v(TAG, "updatePackageParticipantsLocked: " + packageName);
976
977        // brute force but small code size
978        List<PackageInfo> allApps = allAgentPackages();
979        removePackageParticipantsLockedInner(packageName, allApps);
980        addPackageParticipantsLockedInner(packageName, allApps);
981    }
982
983    // Called from the backup task: record that the given app has been successfully
984    // backed up at least once
985    void logBackupComplete(String packageName) {
986        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
987
988        synchronized (mEverStoredApps) {
989            if (!mEverStoredApps.add(packageName)) return;
990
991            RandomAccessFile out = null;
992            try {
993                out = new RandomAccessFile(mEverStored, "rws");
994                out.seek(out.length());
995                out.writeUTF(packageName);
996            } catch (IOException e) {
997                Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
998            } finally {
999                try { if (out != null) out.close(); } catch (IOException e) {}
1000            }
1001        }
1002    }
1003
1004    // Remove our awareness of having ever backed up the given package
1005    void removeEverBackedUp(String packageName) {
1006        if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName + ", new set:");
1007
1008        synchronized (mEverStoredApps) {
1009            // Rewrite the file and rename to overwrite.  If we reboot in the middle,
1010            // we'll recognize on initialization time that the package no longer
1011            // exists and fix it up then.
1012            File tempKnownFile = new File(mBaseStateDir, "processed.new");
1013            RandomAccessFile known = null;
1014            try {
1015                known = new RandomAccessFile(tempKnownFile, "rws");
1016                mEverStoredApps.remove(packageName);
1017                for (String s : mEverStoredApps) {
1018                    known.writeUTF(s);
1019                    if (DEBUG) Slog.v(TAG, "    " + s);
1020                }
1021                known.close();
1022                known = null;
1023                if (!tempKnownFile.renameTo(mEverStored)) {
1024                    throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
1025                }
1026            } catch (IOException e) {
1027                // Bad: we couldn't create the new copy.  For safety's sake we
1028                // abandon the whole process and remove all what's-backed-up
1029                // state entirely, meaning we'll force a backup pass for every
1030                // participant on the next boot or [re]install.
1031                Slog.w(TAG, "Error rewriting " + mEverStored, e);
1032                mEverStoredApps.clear();
1033                tempKnownFile.delete();
1034                mEverStored.delete();
1035            } finally {
1036                try { if (known != null) known.close(); } catch (IOException e) {}
1037            }
1038        }
1039    }
1040
1041    // Persistently record the current and ancestral backup tokens as well
1042    // as the set of packages with data [supposedly] available in the
1043    // ancestral dataset.
1044    void writeRestoreTokens() {
1045        try {
1046            RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
1047
1048            // First, the version number of this record, for futureproofing
1049            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
1050
1051            // Write the ancestral and current tokens
1052            af.writeLong(mAncestralToken);
1053            af.writeLong(mCurrentToken);
1054
1055            // Now write the set of ancestral packages
1056            if (mAncestralPackages == null) {
1057                af.writeInt(-1);
1058            } else {
1059                af.writeInt(mAncestralPackages.size());
1060                if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
1061                for (String pkgName : mAncestralPackages) {
1062                    af.writeUTF(pkgName);
1063                    if (DEBUG) Slog.v(TAG, "   " + pkgName);
1064                }
1065            }
1066            af.close();
1067        } catch (IOException e) {
1068            Slog.w(TAG, "Unable to write token file:", e);
1069        }
1070    }
1071
1072    // Return the given transport
1073    private IBackupTransport getTransport(String transportName) {
1074        synchronized (mTransports) {
1075            IBackupTransport transport = mTransports.get(transportName);
1076            if (transport == null) {
1077                Slog.w(TAG, "Requested unavailable transport: " + transportName);
1078            }
1079            return transport;
1080        }
1081    }
1082
1083    // fire off a backup agent, blocking until it attaches or times out
1084    IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
1085        IBackupAgent agent = null;
1086        synchronized(mAgentConnectLock) {
1087            mConnecting = true;
1088            mConnectedAgent = null;
1089            try {
1090                if (mActivityManager.bindBackupAgent(app, mode)) {
1091                    Slog.d(TAG, "awaiting agent for " + app);
1092
1093                    // success; wait for the agent to arrive
1094                    // only wait 10 seconds for the clear data to happen
1095                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1096                    while (mConnecting && mConnectedAgent == null
1097                            && (System.currentTimeMillis() < timeoutMark)) {
1098                        try {
1099                            mAgentConnectLock.wait(5000);
1100                        } catch (InterruptedException e) {
1101                            // just bail
1102                            return null;
1103                        }
1104                    }
1105
1106                    // if we timed out with no connect, abort and move on
1107                    if (mConnecting == true) {
1108                        Slog.w(TAG, "Timeout waiting for agent " + app);
1109                        return null;
1110                    }
1111                    agent = mConnectedAgent;
1112                }
1113            } catch (RemoteException e) {
1114                // can't happen
1115            }
1116        }
1117        return agent;
1118    }
1119
1120    // clear an application's data, blocking until the operation completes or times out
1121    void clearApplicationDataSynchronous(String packageName) {
1122        // Don't wipe packages marked allowClearUserData=false
1123        try {
1124            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1125            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
1126                if (DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
1127                        + packageName);
1128                return;
1129            }
1130        } catch (NameNotFoundException e) {
1131            Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
1132            return;
1133        }
1134
1135        ClearDataObserver observer = new ClearDataObserver();
1136
1137        synchronized(mClearDataLock) {
1138            mClearingData = true;
1139            try {
1140                mActivityManager.clearApplicationUserData(packageName, observer);
1141            } catch (RemoteException e) {
1142                // can't happen because the activity manager is in this process
1143            }
1144
1145            // only wait 10 seconds for the clear data to happen
1146            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1147            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
1148                try {
1149                    mClearDataLock.wait(5000);
1150                } catch (InterruptedException e) {
1151                    // won't happen, but still.
1152                    mClearingData = false;
1153                }
1154            }
1155        }
1156    }
1157
1158    class ClearDataObserver extends IPackageDataObserver.Stub {
1159        public void onRemoveCompleted(String packageName, boolean succeeded) {
1160            synchronized(mClearDataLock) {
1161                mClearingData = false;
1162                mClearDataLock.notifyAll();
1163            }
1164        }
1165    }
1166
1167    // Get the restore-set token for the best-available restore set for this package:
1168    // the active set if possible, else the ancestral one.  Returns zero if none available.
1169    long getAvailableRestoreToken(String packageName) {
1170        long token = mAncestralToken;
1171        synchronized (mQueueLock) {
1172            if (mEverStoredApps.contains(packageName)) {
1173                token = mCurrentToken;
1174            }
1175        }
1176        return token;
1177    }
1178
1179    // -----
1180    // Utility methods used by the asynchronous-with-timeout backup/restore operations
1181    boolean waitUntilOperationComplete(int token) {
1182        int finalState = OP_PENDING;
1183        synchronized (mCurrentOpLock) {
1184            try {
1185                while ((finalState = mCurrentOperations.get(token, OP_TIMEOUT)) == OP_PENDING) {
1186                    try {
1187                        mCurrentOpLock.wait();
1188                    } catch (InterruptedException e) {}
1189                }
1190            } catch (IndexOutOfBoundsException e) {
1191                // the operation has been mysteriously cleared from our
1192                // bookkeeping -- consider this a success and ignore it.
1193            }
1194        }
1195        mBackupHandler.removeMessages(MSG_TIMEOUT);
1196        if (DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
1197                + " complete: finalState=" + finalState);
1198        return finalState == OP_ACKNOWLEDGED;
1199    }
1200
1201    void prepareOperationTimeout(int token, long interval) {
1202        if (DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
1203                + " interval=" + interval);
1204        mCurrentOperations.put(token, OP_PENDING);
1205        Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
1206        mBackupHandler.sendMessageDelayed(msg, interval);
1207    }
1208
1209    // ----- Back up a set of applications via a worker thread -----
1210
1211    class PerformBackupTask implements Runnable {
1212        private static final String TAG = "PerformBackupThread";
1213        IBackupTransport mTransport;
1214        ArrayList<BackupRequest> mQueue;
1215        File mStateDir;
1216        File mJournal;
1217
1218        public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue,
1219                File journal) {
1220            mTransport = transport;
1221            mQueue = queue;
1222            mJournal = journal;
1223
1224            try {
1225                mStateDir = new File(mBaseStateDir, transport.transportDirName());
1226            } catch (RemoteException e) {
1227                // can't happen; the transport is local
1228            }
1229        }
1230
1231        public void run() {
1232            int status = BackupConstants.TRANSPORT_OK;
1233            long startRealtime = SystemClock.elapsedRealtime();
1234            if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
1235
1236            // Backups run at background priority
1237            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1238
1239            try {
1240                EventLog.writeEvent(EventLogTags.BACKUP_START, mTransport.transportDirName());
1241
1242                // If we haven't stored package manager metadata yet, we must init the transport.
1243                File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
1244                if (status == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
1245                    Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
1246                    resetBackupState(mStateDir);  // Just to make sure.
1247                    status = mTransport.initializeDevice();
1248                    if (status == BackupConstants.TRANSPORT_OK) {
1249                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
1250                    } else {
1251                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
1252                        Slog.e(TAG, "Transport error in initializeDevice()");
1253                    }
1254                }
1255
1256                // The package manager doesn't have a proper <application> etc, but since
1257                // it's running here in the system process we can just set up its agent
1258                // directly and use a synthetic BackupRequest.  We always run this pass
1259                // because it's cheap and this way we guarantee that we don't get out of
1260                // step even if we're selecting among various transports at run time.
1261                if (status == BackupConstants.TRANSPORT_OK) {
1262                    PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
1263                            mPackageManager, allAgentPackages());
1264                    BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
1265                    pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
1266                    status = processOneBackup(pmRequest,
1267                            IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
1268                }
1269
1270                if (status == BackupConstants.TRANSPORT_OK) {
1271                    // Now run all the backups in our queue
1272                    status = doQueuedBackups(mTransport);
1273                }
1274
1275                if (status == BackupConstants.TRANSPORT_OK) {
1276                    // Tell the transport to finish everything it has buffered
1277                    status = mTransport.finishBackup();
1278                    if (status == BackupConstants.TRANSPORT_OK) {
1279                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
1280                        EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, mQueue.size(), millis);
1281                    } else {
1282                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(finish)");
1283                        Slog.e(TAG, "Transport error in finishBackup()");
1284                    }
1285                }
1286
1287                if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
1288                    // The backend reports that our dataset has been wiped.  We need to
1289                    // reset all of our bookkeeping and instead run a new backup pass for
1290                    // everything.
1291                    EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
1292                    resetBackupState(mStateDir);
1293                }
1294            } catch (Exception e) {
1295                Slog.e(TAG, "Error in backup thread", e);
1296                status = BackupConstants.TRANSPORT_ERROR;
1297            } finally {
1298                // If everything actually went through and this is the first time we've
1299                // done a backup, we can now record what the current backup dataset token
1300                // is.
1301                if ((mCurrentToken == 0) && (status != BackupConstants.TRANSPORT_OK)) {
1302                    try {
1303                        mCurrentToken = mTransport.getCurrentRestoreSet();
1304                    } catch (RemoteException e) { /* cannot happen */ }
1305                    writeRestoreTokens();
1306                }
1307
1308                // If things went wrong, we need to re-stage the apps we had expected
1309                // to be backing up in this pass.  This journals the package names in
1310                // the current active pending-backup file, not in the we are holding
1311                // here in mJournal.
1312                if (status != BackupConstants.TRANSPORT_OK) {
1313                    Slog.w(TAG, "Backup pass unsuccessful, restaging");
1314                    for (BackupRequest req : mQueue) {
1315                        dataChanged(req.appInfo.packageName);
1316                    }
1317
1318                    // We also want to reset the backup schedule based on whatever
1319                    // the transport suggests by way of retry/backoff time.
1320                    try {
1321                        startBackupAlarmsLocked(mTransport.requestBackupTime());
1322                    } catch (RemoteException e) { /* cannot happen */ }
1323                }
1324
1325                // Either backup was successful, in which case we of course do not need
1326                // this pass's journal any more; or it failed, in which case we just
1327                // re-enqueued all of these packages in the current active journal.
1328                // Either way, we no longer need this pass's journal.
1329                if (mJournal != null && !mJournal.delete()) {
1330                    Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
1331                }
1332
1333                // Only once we're entirely finished do we release the wakelock
1334                if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
1335                    backupNow();
1336                }
1337
1338                mWakelock.release();
1339            }
1340        }
1341
1342        private int doQueuedBackups(IBackupTransport transport) {
1343            for (BackupRequest request : mQueue) {
1344                Slog.d(TAG, "starting agent for backup of " + request);
1345
1346                IBackupAgent agent = null;
1347                int mode = (request.fullBackup)
1348                        ? IApplicationThread.BACKUP_MODE_FULL
1349                        : IApplicationThread.BACKUP_MODE_INCREMENTAL;
1350                try {
1351                    agent = bindToAgentSynchronous(request.appInfo, mode);
1352                    if (agent != null) {
1353                        int result = processOneBackup(request, agent, transport);
1354                        if (result != BackupConstants.TRANSPORT_OK) return result;
1355                    }
1356                } catch (SecurityException ex) {
1357                    // Try for the next one.
1358                    Slog.d(TAG, "error in bind/backup", ex);
1359                } finally {
1360                    try {  // unbind even on timeout, just in case
1361                        mActivityManager.unbindBackupAgent(request.appInfo);
1362                    } catch (RemoteException e) {}
1363                }
1364            }
1365
1366            return BackupConstants.TRANSPORT_OK;
1367        }
1368
1369        private int processOneBackup(BackupRequest request, IBackupAgent agent,
1370                IBackupTransport transport) {
1371            final String packageName = request.appInfo.packageName;
1372            if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName);
1373
1374            File savedStateName = new File(mStateDir, packageName);
1375            File backupDataName = new File(mDataDir, packageName + ".data");
1376            File newStateName = new File(mStateDir, packageName + ".new");
1377
1378            ParcelFileDescriptor savedState = null;
1379            ParcelFileDescriptor backupData = null;
1380            ParcelFileDescriptor newState = null;
1381
1382            PackageInfo packInfo;
1383            int token = mTokenGenerator.nextInt();
1384            try {
1385                // Look up the package info & signatures.  This is first so that if it
1386                // throws an exception, there's no file setup yet that would need to
1387                // be unraveled.
1388                if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
1389                    // The metadata 'package' is synthetic
1390                    packInfo = new PackageInfo();
1391                    packInfo.packageName = packageName;
1392                } else {
1393                    packInfo = mPackageManager.getPackageInfo(packageName,
1394                        PackageManager.GET_SIGNATURES);
1395                }
1396
1397                // In a full backup, we pass a null ParcelFileDescriptor as
1398                // the saved-state "file"
1399                if (!request.fullBackup) {
1400                    savedState = ParcelFileDescriptor.open(savedStateName,
1401                            ParcelFileDescriptor.MODE_READ_ONLY |
1402                            ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
1403                }
1404
1405                backupData = ParcelFileDescriptor.open(backupDataName,
1406                        ParcelFileDescriptor.MODE_READ_WRITE |
1407                        ParcelFileDescriptor.MODE_CREATE |
1408                        ParcelFileDescriptor.MODE_TRUNCATE);
1409
1410                newState = ParcelFileDescriptor.open(newStateName,
1411                        ParcelFileDescriptor.MODE_READ_WRITE |
1412                        ParcelFileDescriptor.MODE_CREATE |
1413                        ParcelFileDescriptor.MODE_TRUNCATE);
1414
1415                // Initiate the target's backup pass
1416                prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
1417                agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder);
1418                boolean success = waitUntilOperationComplete(token);
1419
1420                if (!success) {
1421                    // timeout -- bail out into the failed-transaction logic
1422                    throw new RuntimeException("Backup timeout");
1423                }
1424
1425                logBackupComplete(packageName);
1426                if (DEBUG) Slog.v(TAG, "doBackup() success");
1427            } catch (Exception e) {
1428                Slog.e(TAG, "Error backing up " + packageName, e);
1429                EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString());
1430                backupDataName.delete();
1431                newStateName.delete();
1432                return BackupConstants.TRANSPORT_ERROR;
1433            } finally {
1434                try { if (savedState != null) savedState.close(); } catch (IOException e) {}
1435                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1436                try { if (newState != null) newState.close(); } catch (IOException e) {}
1437                savedState = backupData = newState = null;
1438                synchronized (mCurrentOpLock) {
1439                    mCurrentOperations.clear();
1440                }
1441            }
1442
1443            // Now propagate the newly-backed-up data to the transport
1444            int result = BackupConstants.TRANSPORT_OK;
1445            try {
1446                int size = (int) backupDataName.length();
1447                if (size > 0) {
1448                    if (result == BackupConstants.TRANSPORT_OK) {
1449                        backupData = ParcelFileDescriptor.open(backupDataName,
1450                                ParcelFileDescriptor.MODE_READ_ONLY);
1451                        result = transport.performBackup(packInfo, backupData);
1452                    }
1453
1454                    // TODO - We call finishBackup() for each application backed up, because
1455                    // we need to know now whether it succeeded or failed.  Instead, we should
1456                    // hold off on finishBackup() until the end, which implies holding off on
1457                    // renaming *all* the output state files (see below) until that happens.
1458
1459                    if (result == BackupConstants.TRANSPORT_OK) {
1460                        result = transport.finishBackup();
1461                    }
1462                } else {
1463                    if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
1464                }
1465
1466                // After successful transport, delete the now-stale data
1467                // and juggle the files so that next time we supply the agent
1468                // with the new state file it just created.
1469                if (result == BackupConstants.TRANSPORT_OK) {
1470                    backupDataName.delete();
1471                    newStateName.renameTo(savedStateName);
1472                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, packageName, size);
1473                } else {
1474                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
1475                }
1476            } catch (Exception e) {
1477                Slog.e(TAG, "Transport error backing up " + packageName, e);
1478                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
1479                result = BackupConstants.TRANSPORT_ERROR;
1480            } finally {
1481                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1482            }
1483
1484            return result;
1485        }
1486    }
1487
1488
1489    // ----- Restore handling -----
1490
1491    private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
1492        // If the target resides on the system partition, we allow it to restore
1493        // data from the like-named package in a restore set even if the signatures
1494        // do not match.  (Unlike general applications, those flashed to the system
1495        // partition will be signed with the device's platform certificate, so on
1496        // different phones the same system app will have different signatures.)
1497        if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
1498            if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
1499            return true;
1500        }
1501
1502        // Allow unsigned apps, but not signed on one device and unsigned on the other
1503        // !!! TODO: is this the right policy?
1504        Signature[] deviceSigs = target.signatures;
1505        if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
1506                + " device=" + deviceSigs);
1507        if ((storedSigs == null || storedSigs.length == 0)
1508                && (deviceSigs == null || deviceSigs.length == 0)) {
1509            return true;
1510        }
1511        if (storedSigs == null || deviceSigs == null) {
1512            return false;
1513        }
1514
1515        // !!! TODO: this demands that every stored signature match one
1516        // that is present on device, and does not demand the converse.
1517        // Is this this right policy?
1518        int nStored = storedSigs.length;
1519        int nDevice = deviceSigs.length;
1520
1521        for (int i=0; i < nStored; i++) {
1522            boolean match = false;
1523            for (int j=0; j < nDevice; j++) {
1524                if (storedSigs[i].equals(deviceSigs[j])) {
1525                    match = true;
1526                    break;
1527                }
1528            }
1529            if (!match) {
1530                return false;
1531            }
1532        }
1533        return true;
1534    }
1535
1536    class PerformRestoreTask implements Runnable {
1537        private IBackupTransport mTransport;
1538        private IRestoreObserver mObserver;
1539        private long mToken;
1540        private PackageInfo mTargetPackage;
1541        private File mStateDir;
1542        private int mPmToken;
1543
1544        class RestoreRequest {
1545            public PackageInfo app;
1546            public int storedAppVersion;
1547
1548            RestoreRequest(PackageInfo _app, int _version) {
1549                app = _app;
1550                storedAppVersion = _version;
1551            }
1552        }
1553
1554        PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
1555                long restoreSetToken, PackageInfo targetPackage, int pmToken) {
1556            mTransport = transport;
1557            mObserver = observer;
1558            mToken = restoreSetToken;
1559            mTargetPackage = targetPackage;
1560            mPmToken = pmToken;
1561
1562            try {
1563                mStateDir = new File(mBaseStateDir, transport.transportDirName());
1564            } catch (RemoteException e) {
1565                // can't happen; the transport is local
1566            }
1567        }
1568
1569        public void run() {
1570            long startRealtime = SystemClock.elapsedRealtime();
1571            if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport
1572                    + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
1573                    + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken);
1574
1575            PackageManagerBackupAgent pmAgent = null;
1576            int error = -1; // assume error
1577
1578            // build the set of apps to restore
1579            try {
1580                // TODO: Log this before getAvailableRestoreSets, somehow
1581                EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken);
1582
1583                // Get the list of all packages which have backup enabled.
1584                // (Include the Package Manager metadata pseudo-package first.)
1585                ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
1586                PackageInfo omPackage = new PackageInfo();
1587                omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
1588                restorePackages.add(omPackage);
1589
1590                List<PackageInfo> agentPackages = allAgentPackages();
1591                if (mTargetPackage == null) {
1592                    restorePackages.addAll(agentPackages);
1593                } else {
1594                    // Just one package to attempt restore of
1595                    restorePackages.add(mTargetPackage);
1596                }
1597
1598                // let the observer know that we're running
1599                if (mObserver != null) {
1600                    try {
1601                        // !!! TODO: get an actual count from the transport after
1602                        // its startRestore() runs?
1603                        mObserver.restoreStarting(restorePackages.size());
1604                    } catch (RemoteException e) {
1605                        Slog.d(TAG, "Restore observer died at restoreStarting");
1606                        mObserver = null;
1607                    }
1608                }
1609
1610                if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) !=
1611                        BackupConstants.TRANSPORT_OK) {
1612                    Slog.e(TAG, "Error starting restore operation");
1613                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
1614                    return;
1615                }
1616
1617                String packageName = mTransport.nextRestorePackage();
1618                if (packageName == null) {
1619                    Slog.e(TAG, "Error getting first restore package");
1620                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
1621                    return;
1622                } else if (packageName.equals("")) {
1623                    Slog.i(TAG, "No restore data available");
1624                    int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
1625                    EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
1626                    return;
1627                } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
1628                    Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
1629                          + "\", found only \"" + packageName + "\"");
1630                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
1631                            "Package manager data missing");
1632                    return;
1633                }
1634
1635                // Pull the Package Manager metadata from the restore set first
1636                pmAgent = new PackageManagerBackupAgent(
1637                        mPackageManager, agentPackages);
1638                processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
1639
1640                // Verify that the backup set includes metadata.  If not, we can't do
1641                // signature/version verification etc, so we simply do not proceed with
1642                // the restore operation.
1643                if (!pmAgent.hasMetadata()) {
1644                    Slog.e(TAG, "No restore metadata available, so not restoring settings");
1645                    EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
1646                            "Package manager restore metadata missing");
1647                    return;
1648                }
1649
1650                int count = 0;
1651                for (;;) {
1652                    packageName = mTransport.nextRestorePackage();
1653
1654                    if (packageName == null) {
1655                        Slog.e(TAG, "Error getting next restore package");
1656                        EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
1657                        return;
1658                    } else if (packageName.equals("")) {
1659                        if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
1660                        break;
1661                    }
1662
1663                    if (mObserver != null) {
1664                        try {
1665                            mObserver.onUpdate(count, packageName);
1666                        } catch (RemoteException e) {
1667                            Slog.d(TAG, "Restore observer died in onUpdate");
1668                            mObserver = null;
1669                        }
1670                    }
1671
1672                    Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
1673                    if (metaInfo == null) {
1674                        Slog.e(TAG, "Missing metadata for " + packageName);
1675                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
1676                                "Package metadata missing");
1677                        continue;
1678                    }
1679
1680                    PackageInfo packageInfo;
1681                    try {
1682                        int flags = PackageManager.GET_SIGNATURES;
1683                        packageInfo = mPackageManager.getPackageInfo(packageName, flags);
1684                    } catch (NameNotFoundException e) {
1685                        Slog.e(TAG, "Invalid package restoring data", e);
1686                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
1687                                "Package missing on device");
1688                        continue;
1689                    }
1690
1691                    if (metaInfo.versionCode > packageInfo.versionCode) {
1692                        // Data is from a "newer" version of the app than we have currently
1693                        // installed.  If the app has not declared that it is prepared to
1694                        // handle this case, we do not attempt the restore.
1695                        if ((packageInfo.applicationInfo.flags
1696                                & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
1697                            String message = "Version " + metaInfo.versionCode
1698                                    + " > installed version " + packageInfo.versionCode;
1699                            Slog.w(TAG, "Package " + packageName + ": " + message);
1700                            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
1701                                    packageName, message);
1702                            continue;
1703                        } else {
1704                            if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
1705                                    + " > installed " + packageInfo.versionCode
1706                                    + " but restoreAnyVersion");
1707                        }
1708                    }
1709
1710                    if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
1711                        Slog.w(TAG, "Signature mismatch restoring " + packageName);
1712                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
1713                                "Signature mismatch");
1714                        continue;
1715                    }
1716
1717                    if (DEBUG) Slog.v(TAG, "Package " + packageName
1718                            + " restore version [" + metaInfo.versionCode
1719                            + "] is compatible with installed version ["
1720                            + packageInfo.versionCode + "]");
1721
1722                    // Then set up and bind the agent
1723                    IBackupAgent agent = bindToAgentSynchronous(
1724                            packageInfo.applicationInfo,
1725                            IApplicationThread.BACKUP_MODE_INCREMENTAL);
1726                    if (agent == null) {
1727                        Slog.w(TAG, "Can't find backup agent for " + packageName);
1728                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
1729                                "Restore agent missing");
1730                        continue;
1731                    }
1732
1733                    // And then finally run the restore on this agent
1734                    try {
1735                        processOneRestore(packageInfo, metaInfo.versionCode, agent);
1736                        ++count;
1737                    } finally {
1738                        // unbind and tidy up even on timeout or failure, just in case
1739                        mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
1740
1741                        // The agent was probably running with a stub Application object,
1742                        // which isn't a valid run mode for the main app logic.  Shut
1743                        // down the app so that next time it's launched, it gets the
1744                        // usual full initialization.  Note that this is only done for
1745                        // full-system restores: when a single app has requested a restore,
1746                        // it is explicitly not killed following that operation.
1747                        if (mTargetPackage == null && (packageInfo.applicationInfo.flags
1748                                & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
1749                            if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
1750                                    + packageInfo.applicationInfo.processName);
1751                            mActivityManager.killApplicationProcess(
1752                                    packageInfo.applicationInfo.processName,
1753                                    packageInfo.applicationInfo.uid);
1754                        }
1755                    }
1756                }
1757
1758                // if we get this far, report success to the observer
1759                error = 0;
1760                int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
1761                EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, count, millis);
1762            } catch (Exception e) {
1763                Slog.e(TAG, "Error in restore thread", e);
1764            } finally {
1765                if (DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
1766
1767                try {
1768                    mTransport.finishRestore();
1769                } catch (RemoteException e) {
1770                    Slog.e(TAG, "Error finishing restore", e);
1771                }
1772
1773                if (mObserver != null) {
1774                    try {
1775                        mObserver.restoreFinished(error);
1776                    } catch (RemoteException e) {
1777                        Slog.d(TAG, "Restore observer died at restoreFinished");
1778                    }
1779                }
1780
1781                // If this was a restoreAll operation, record that this was our
1782                // ancestral dataset, as well as the set of apps that are possibly
1783                // restoreable from the dataset
1784                if (mTargetPackage == null && pmAgent != null) {
1785                    mAncestralPackages = pmAgent.getRestoredPackages();
1786                    mAncestralToken = mToken;
1787                    writeRestoreTokens();
1788                }
1789
1790                // We must under all circumstances tell the Package Manager to
1791                // proceed with install notifications if it's waiting for us.
1792                if (mPmToken > 0) {
1793                    if (DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
1794                    try {
1795                        mPackageManagerBinder.finishPackageInstall(mPmToken);
1796                    } catch (RemoteException e) { /* can't happen */ }
1797                }
1798
1799                // done; we can finally release the wakelock
1800                mWakelock.release();
1801            }
1802        }
1803
1804        // Do the guts of a restore of one application, using mTransport.getRestoreData().
1805        void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
1806            // !!! TODO: actually run the restore through mTransport
1807            final String packageName = app.packageName;
1808
1809            if (DEBUG) Slog.d(TAG, "processOneRestore packageName=" + packageName);
1810
1811            // !!! TODO: get the dirs from the transport
1812            File backupDataName = new File(mDataDir, packageName + ".restore");
1813            File newStateName = new File(mStateDir, packageName + ".new");
1814            File savedStateName = new File(mStateDir, packageName);
1815
1816            ParcelFileDescriptor backupData = null;
1817            ParcelFileDescriptor newState = null;
1818
1819            int token = mTokenGenerator.nextInt();
1820            try {
1821                // Run the transport's restore pass
1822                backupData = ParcelFileDescriptor.open(backupDataName,
1823                            ParcelFileDescriptor.MODE_READ_WRITE |
1824                            ParcelFileDescriptor.MODE_CREATE |
1825                            ParcelFileDescriptor.MODE_TRUNCATE);
1826
1827                if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
1828                    Slog.e(TAG, "Error getting restore data for " + packageName);
1829                    EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
1830                    return;
1831                }
1832
1833                // Okay, we have the data.  Now have the agent do the restore.
1834                backupData.close();
1835                backupData = ParcelFileDescriptor.open(backupDataName,
1836                            ParcelFileDescriptor.MODE_READ_ONLY);
1837
1838                newState = ParcelFileDescriptor.open(newStateName,
1839                            ParcelFileDescriptor.MODE_READ_WRITE |
1840                            ParcelFileDescriptor.MODE_CREATE |
1841                            ParcelFileDescriptor.MODE_TRUNCATE);
1842
1843                // Kick off the restore, checking for hung agents
1844                prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL);
1845                agent.doRestore(backupData, appVersionCode, newState, token, mBackupManagerBinder);
1846                boolean success = waitUntilOperationComplete(token);
1847
1848                if (!success) {
1849                    throw new RuntimeException("restore timeout");
1850                }
1851
1852                // if everything went okay, remember the recorded state now
1853                //
1854                // !!! TODO: the restored data should be migrated on the server
1855                // side into the current dataset.  In that case the new state file
1856                // we just created would reflect the data already extant in the
1857                // backend, so there'd be nothing more to do.  Until that happens,
1858                // however, we need to make sure that we record the data to the
1859                // current backend dataset.  (Yes, this means shipping the data over
1860                // the wire in both directions.  That's bad, but consistency comes
1861                // first, then efficiency.)  Once we introduce server-side data
1862                // migration to the newly-restored device's dataset, we will change
1863                // the following from a discard of the newly-written state to the
1864                // "correct" operation of renaming into the canonical state blob.
1865                newStateName.delete();                      // TODO: remove; see above comment
1866                //newStateName.renameTo(savedStateName);    // TODO: replace with this
1867
1868                int size = (int) backupDataName.length();
1869                EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, packageName, size);
1870            } catch (Exception e) {
1871                Slog.e(TAG, "Error restoring data for " + packageName, e);
1872                EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
1873
1874                // If the agent fails restore, it might have put the app's data
1875                // into an incoherent state.  For consistency we wipe its data
1876                // again in this case before propagating the exception
1877                clearApplicationDataSynchronous(packageName);
1878            } finally {
1879                backupDataName.delete();
1880                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1881                try { if (newState != null) newState.close(); } catch (IOException e) {}
1882                backupData = newState = null;
1883                mCurrentOperations.delete(token);
1884            }
1885        }
1886    }
1887
1888    class PerformClearTask implements Runnable {
1889        IBackupTransport mTransport;
1890        PackageInfo mPackage;
1891
1892        PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
1893            mTransport = transport;
1894            mPackage = packageInfo;
1895        }
1896
1897        public void run() {
1898            try {
1899                // Clear the on-device backup state to ensure a full backup next time
1900                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
1901                File stateFile = new File(stateDir, mPackage.packageName);
1902                stateFile.delete();
1903
1904                // Tell the transport to remove all the persistent storage for the app
1905                // TODO - need to handle failures
1906                mTransport.clearBackupData(mPackage);
1907            } catch (RemoteException e) {
1908                // can't happen; the transport is local
1909            } finally {
1910                try {
1911                    // TODO - need to handle failures
1912                    mTransport.finishBackup();
1913                } catch (RemoteException e) {
1914                    // can't happen; the transport is local
1915                }
1916
1917                // Last but not least, release the cpu
1918                mWakelock.release();
1919            }
1920        }
1921    }
1922
1923    class PerformInitializeTask implements Runnable {
1924        HashSet<String> mQueue;
1925
1926        PerformInitializeTask(HashSet<String> transportNames) {
1927            mQueue = transportNames;
1928        }
1929
1930        public void run() {
1931            try {
1932                for (String transportName : mQueue) {
1933                    IBackupTransport transport = getTransport(transportName);
1934                    if (transport == null) {
1935                        Slog.e(TAG, "Requested init for " + transportName + " but not found");
1936                        continue;
1937                    }
1938
1939                    Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
1940                    EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
1941                    long startRealtime = SystemClock.elapsedRealtime();
1942                    int status = transport.initializeDevice();
1943
1944                    if (status == BackupConstants.TRANSPORT_OK) {
1945                        status = transport.finishBackup();
1946                    }
1947
1948                    // Okay, the wipe really happened.  Clean up our local bookkeeping.
1949                    if (status == BackupConstants.TRANSPORT_OK) {
1950                        Slog.i(TAG, "Device init successful");
1951                        int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
1952                        EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
1953                        resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
1954                        EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
1955                        synchronized (mQueueLock) {
1956                            recordInitPendingLocked(false, transportName);
1957                        }
1958                    } else {
1959                        // If this didn't work, requeue this one and try again
1960                        // after a suitable interval
1961                        Slog.e(TAG, "Transport error in initializeDevice()");
1962                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
1963                        synchronized (mQueueLock) {
1964                            recordInitPendingLocked(true, transportName);
1965                        }
1966                        // do this via another alarm to make sure of the wakelock states
1967                        long delay = transport.requestBackupTime();
1968                        if (DEBUG) Slog.w(TAG, "init failed on "
1969                                + transportName + " resched in " + delay);
1970                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1971                                System.currentTimeMillis() + delay, mRunInitIntent);
1972                    }
1973                }
1974            } catch (RemoteException e) {
1975                // can't happen; the transports are local
1976            } catch (Exception e) {
1977                Slog.e(TAG, "Unexpected error performing init", e);
1978            } finally {
1979                // Done; release the wakelock
1980                mWakelock.release();
1981            }
1982        }
1983    }
1984
1985
1986    // ----- IBackupManager binder interface -----
1987
1988    public void dataChanged(String packageName) {
1989        // Record that we need a backup pass for the caller.  Since multiple callers
1990        // may share a uid, we need to note all candidates within that uid and schedule
1991        // a backup pass for each of them.
1992        EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
1993
1994        // If the caller does not hold the BACKUP permission, it can only request a
1995        // backup of its own data.
1996        HashSet<ApplicationInfo> targets;
1997        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
1998                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
1999            targets = mBackupParticipants.get(Binder.getCallingUid());
2000        } else {
2001            // a caller with full permission can ask to back up any participating app
2002            // !!! TODO: allow backup of ANY app?
2003            targets = new HashSet<ApplicationInfo>();
2004            int N = mBackupParticipants.size();
2005            for (int i = 0; i < N; i++) {
2006                HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
2007                if (s != null) {
2008                    targets.addAll(s);
2009                }
2010            }
2011        }
2012        if (targets != null) {
2013            synchronized (mQueueLock) {
2014                // Note that this client has made data changes that need to be backed up
2015                for (ApplicationInfo app : targets) {
2016                    // validate the caller-supplied package name against the known set of
2017                    // packages associated with this uid
2018                    if (app.packageName.equals(packageName)) {
2019                        // Add the caller to the set of pending backups.  If there is
2020                        // one already there, then overwrite it, but no harm done.
2021                        BackupRequest req = new BackupRequest(app, false);
2022                        if (mPendingBackups.put(app, req) == null) {
2023                            // Journal this request in case of crash.  The put()
2024                            // operation returned null when this package was not already
2025                            // in the set; we want to avoid touching the disk redundantly.
2026                            writeToJournalLocked(packageName);
2027
2028                            if (DEBUG) {
2029                                int numKeys = mPendingBackups.size();
2030                                Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
2031                                for (BackupRequest b : mPendingBackups.values()) {
2032                                    Slog.d(TAG, "    + " + b + " agent=" + b.appInfo.backupAgentName);
2033                                }
2034                            }
2035                        }
2036                    }
2037                }
2038            }
2039        } else {
2040            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
2041                    + " uid=" + Binder.getCallingUid());
2042        }
2043    }
2044
2045    private void writeToJournalLocked(String str) {
2046        RandomAccessFile out = null;
2047        try {
2048            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
2049            out = new RandomAccessFile(mJournal, "rws");
2050            out.seek(out.length());
2051            out.writeUTF(str);
2052        } catch (IOException e) {
2053            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
2054            mJournal = null;
2055        } finally {
2056            try { if (out != null) out.close(); } catch (IOException e) {}
2057        }
2058    }
2059
2060    // Clear the given package's backup data from the current transport
2061    public void clearBackupData(String packageName) {
2062        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName);
2063        PackageInfo info;
2064        try {
2065            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
2066        } catch (NameNotFoundException e) {
2067            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
2068            return;
2069        }
2070
2071        // If the caller does not hold the BACKUP permission, it can only request a
2072        // wipe of its own backed-up data.
2073        HashSet<ApplicationInfo> apps;
2074        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
2075                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
2076            apps = mBackupParticipants.get(Binder.getCallingUid());
2077        } else {
2078            // a caller with full permission can ask to back up any participating app
2079            // !!! TODO: allow data-clear of ANY app?
2080            if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
2081            apps = new HashSet<ApplicationInfo>();
2082            int N = mBackupParticipants.size();
2083            for (int i = 0; i < N; i++) {
2084                HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
2085                if (s != null) {
2086                    apps.addAll(s);
2087                }
2088            }
2089        }
2090
2091        // now find the given package in the set of candidate apps
2092        for (ApplicationInfo app : apps) {
2093            if (app.packageName.equals(packageName)) {
2094                if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
2095                // found it; fire off the clear request
2096                synchronized (mQueueLock) {
2097                    long oldId = Binder.clearCallingIdentity();
2098                    mWakelock.acquire();
2099                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
2100                            new ClearParams(getTransport(mCurrentTransport), info));
2101                    mBackupHandler.sendMessage(msg);
2102                    Binder.restoreCallingIdentity(oldId);
2103                }
2104                break;
2105            }
2106        }
2107    }
2108
2109    // Run a backup pass immediately for any applications that have declared
2110    // that they have pending updates.
2111    public void backupNow() {
2112        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
2113
2114        if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
2115        synchronized (mQueueLock) {
2116            // Because the alarms we are using can jitter, and we want an *immediate*
2117            // backup pass to happen, we restart the timer beginning with "next time,"
2118            // then manually fire the backup trigger intent ourselves.
2119            startBackupAlarmsLocked(BACKUP_INTERVAL);
2120            try {
2121                mRunBackupIntent.send();
2122            } catch (PendingIntent.CanceledException e) {
2123                // should never happen
2124                Slog.e(TAG, "run-backup intent cancelled!");
2125            }
2126        }
2127    }
2128
2129    // Enable/disable the backup service
2130    public void setBackupEnabled(boolean enable) {
2131        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2132                "setBackupEnabled");
2133
2134        Slog.i(TAG, "Backup enabled => " + enable);
2135
2136        boolean wasEnabled = mEnabled;
2137        synchronized (this) {
2138            Settings.Secure.putInt(mContext.getContentResolver(),
2139                    Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
2140            mEnabled = enable;
2141        }
2142
2143        synchronized (mQueueLock) {
2144            if (enable && !wasEnabled && mProvisioned) {
2145                // if we've just been enabled, start scheduling backup passes
2146                startBackupAlarmsLocked(BACKUP_INTERVAL);
2147            } else if (!enable) {
2148                // No longer enabled, so stop running backups
2149                if (DEBUG) Slog.i(TAG, "Opting out of backup");
2150
2151                mAlarmManager.cancel(mRunBackupIntent);
2152
2153                // This also constitutes an opt-out, so we wipe any data for
2154                // this device from the backend.  We start that process with
2155                // an alarm in order to guarantee wakelock states.
2156                if (wasEnabled && mProvisioned) {
2157                    // NOTE: we currently flush every registered transport, not just
2158                    // the currently-active one.
2159                    HashSet<String> allTransports;
2160                    synchronized (mTransports) {
2161                        allTransports = new HashSet<String>(mTransports.keySet());
2162                    }
2163                    // build the set of transports for which we are posting an init
2164                    for (String transport : allTransports) {
2165                        recordInitPendingLocked(true, transport);
2166                    }
2167                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
2168                            mRunInitIntent);
2169                }
2170            }
2171        }
2172    }
2173
2174    // Enable/disable automatic restore of app data at install time
2175    public void setAutoRestore(boolean doAutoRestore) {
2176        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2177        "setBackupEnabled");
2178
2179        Slog.i(TAG, "Auto restore => " + doAutoRestore);
2180
2181        synchronized (this) {
2182            Settings.Secure.putInt(mContext.getContentResolver(),
2183                    Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
2184            mAutoRestore = doAutoRestore;
2185        }
2186    }
2187
2188    // Mark the backup service as having been provisioned
2189    public void setBackupProvisioned(boolean available) {
2190        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2191                "setBackupProvisioned");
2192
2193        boolean wasProvisioned = mProvisioned;
2194        synchronized (this) {
2195            Settings.Secure.putInt(mContext.getContentResolver(),
2196                    Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
2197            mProvisioned = available;
2198        }
2199
2200        synchronized (mQueueLock) {
2201            if (available && !wasProvisioned && mEnabled) {
2202                // we're now good to go, so start the backup alarms
2203                startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
2204            } else if (!available) {
2205                // No longer enabled, so stop running backups
2206                Slog.w(TAG, "Backup service no longer provisioned");
2207                mAlarmManager.cancel(mRunBackupIntent);
2208            }
2209        }
2210    }
2211
2212    private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
2213        // We used to use setInexactRepeating(), but that may be linked to
2214        // backups running at :00 more often than not, creating load spikes.
2215        // Schedule at an exact time for now, and also add a bit of "fuzz".
2216
2217        Random random = new Random();
2218        long when = System.currentTimeMillis() + delayBeforeFirstBackup +
2219                random.nextInt(FUZZ_MILLIS);
2220        mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
2221                BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
2222        mNextBackupPass = when;
2223    }
2224
2225    // Report whether the backup mechanism is currently enabled
2226    public boolean isBackupEnabled() {
2227        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
2228        return mEnabled;    // no need to synchronize just to read it
2229    }
2230
2231    // Report the name of the currently active transport
2232    public String getCurrentTransport() {
2233        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2234                "getCurrentTransport");
2235        if (DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
2236        return mCurrentTransport;
2237    }
2238
2239    // Report all known, available backup transports
2240    public String[] listAllTransports() {
2241        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
2242
2243        String[] list = null;
2244        ArrayList<String> known = new ArrayList<String>();
2245        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
2246            if (entry.getValue() != null) {
2247                known.add(entry.getKey());
2248            }
2249        }
2250
2251        if (known.size() > 0) {
2252            list = new String[known.size()];
2253            known.toArray(list);
2254        }
2255        return list;
2256    }
2257
2258    // Select which transport to use for the next backup operation.  If the given
2259    // name is not one of the available transports, no action is taken and the method
2260    // returns null.
2261    public String selectBackupTransport(String transport) {
2262        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
2263
2264        synchronized (mTransports) {
2265            String prevTransport = null;
2266            if (mTransports.get(transport) != null) {
2267                prevTransport = mCurrentTransport;
2268                mCurrentTransport = transport;
2269                Settings.Secure.putString(mContext.getContentResolver(),
2270                        Settings.Secure.BACKUP_TRANSPORT, transport);
2271                Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
2272                        + " returning " + prevTransport);
2273            } else {
2274                Slog.w(TAG, "Attempt to select unavailable transport " + transport);
2275            }
2276            return prevTransport;
2277        }
2278    }
2279
2280    // Callback: a requested backup agent has been instantiated.  This should only
2281    // be called from the Activity Manager.
2282    public void agentConnected(String packageName, IBinder agentBinder) {
2283        synchronized(mAgentConnectLock) {
2284            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
2285                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
2286                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
2287                mConnectedAgent = agent;
2288                mConnecting = false;
2289            } else {
2290                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
2291                        + " claiming agent connected");
2292            }
2293            mAgentConnectLock.notifyAll();
2294        }
2295    }
2296
2297    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
2298    // If the agent failed to come up in the first place, the agentBinder argument
2299    // will be null.  This should only be called from the Activity Manager.
2300    public void agentDisconnected(String packageName) {
2301        // TODO: handle backup being interrupted
2302        synchronized(mAgentConnectLock) {
2303            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
2304                mConnectedAgent = null;
2305                mConnecting = false;
2306            } else {
2307                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
2308                        + " claiming agent disconnected");
2309            }
2310            mAgentConnectLock.notifyAll();
2311        }
2312    }
2313
2314    // An application being installed will need a restore pass, then the Package Manager
2315    // will need to be told when the restore is finished.
2316    public void restoreAtInstall(String packageName, int token) {
2317        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
2318            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
2319                    + " attemping install-time restore");
2320            return;
2321        }
2322
2323        long restoreSet = getAvailableRestoreToken(packageName);
2324        if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
2325                + " token=" + Integer.toHexString(token));
2326
2327        if (mAutoRestore && mProvisioned && restoreSet != 0) {
2328            // okay, we're going to attempt a restore of this package from this restore set.
2329            // The eventual message back into the Package Manager to run the post-install
2330            // steps for 'token' will be issued from the restore handling code.
2331
2332            // We can use a synthetic PackageInfo here because:
2333            //   1. We know it's valid, since the Package Manager supplied the name
2334            //   2. Only the packageName field will be used by the restore code
2335            PackageInfo pkg = new PackageInfo();
2336            pkg.packageName = packageName;
2337
2338            mWakelock.acquire();
2339            Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
2340            msg.obj = new RestoreParams(getTransport(mCurrentTransport), null,
2341                    restoreSet, pkg, token);
2342            mBackupHandler.sendMessage(msg);
2343        } else {
2344            // Auto-restore disabled or no way to attempt a restore; just tell the Package
2345            // Manager to proceed with the post-install handling for this package.
2346            if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore");
2347            try {
2348                mPackageManagerBinder.finishPackageInstall(token);
2349            } catch (RemoteException e) { /* can't happen */ }
2350        }
2351    }
2352
2353    // Hand off a restore session
2354    public IRestoreSession beginRestoreSession(String transport) {
2355        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
2356
2357        synchronized(this) {
2358            if (mActiveRestoreSession != null) {
2359                Slog.d(TAG, "Restore session requested but one already active");
2360                return null;
2361            }
2362            mActiveRestoreSession = new ActiveRestoreSession(transport);
2363        }
2364        return mActiveRestoreSession;
2365    }
2366
2367    // Note that a currently-active backup agent has notified us that it has
2368    // completed the given outstanding asynchronous backup/restore operation.
2369    public void opComplete(int token) {
2370        synchronized (mCurrentOpLock) {
2371            if (DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
2372            mCurrentOperations.put(token, OP_ACKNOWLEDGED);
2373            mCurrentOpLock.notifyAll();
2374        }
2375    }
2376
2377    // ----- Restore session -----
2378
2379    class ActiveRestoreSession extends IRestoreSession.Stub {
2380        private static final String TAG = "RestoreSession";
2381
2382        private IBackupTransport mRestoreTransport = null;
2383        RestoreSet[] mRestoreSets = null;
2384
2385        ActiveRestoreSession(String transport) {
2386            mRestoreTransport = getTransport(transport);
2387        }
2388
2389        // --- Binder interface ---
2390        public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
2391            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2392                    "getAvailableRestoreSets");
2393            if (observer == null) {
2394                throw new IllegalArgumentException("Observer must not be null");
2395            }
2396
2397            long oldId = Binder.clearCallingIdentity();
2398            try {
2399                if (mRestoreTransport == null) {
2400                    Slog.w(TAG, "Null transport getting restore sets");
2401                    return -1;
2402                }
2403                // spin off the transport request to our service thread
2404                mWakelock.acquire();
2405                Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
2406                        new RestoreGetSetsParams(mRestoreTransport, this, observer));
2407                mBackupHandler.sendMessage(msg);
2408                return 0;
2409            } catch (Exception e) {
2410                Slog.e(TAG, "Error in getAvailableRestoreSets", e);
2411                return -1;
2412            } finally {
2413                Binder.restoreCallingIdentity(oldId);
2414            }
2415        }
2416
2417        public synchronized int restoreAll(long token, IRestoreObserver observer) {
2418            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2419                    "performRestore");
2420
2421            if (DEBUG) Slog.d(TAG, "performRestore token=" + Long.toHexString(token)
2422                    + " observer=" + observer);
2423
2424            if (mRestoreTransport == null || mRestoreSets == null) {
2425                Slog.e(TAG, "Ignoring performRestore() with no restore set");
2426                return -1;
2427            }
2428
2429            synchronized (mQueueLock) {
2430                for (int i = 0; i < mRestoreSets.length; i++) {
2431                    if (token == mRestoreSets[i].token) {
2432                        long oldId = Binder.clearCallingIdentity();
2433                        mWakelock.acquire();
2434                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
2435                        msg.obj = new RestoreParams(mRestoreTransport, observer, token);
2436                        mBackupHandler.sendMessage(msg);
2437                        Binder.restoreCallingIdentity(oldId);
2438                        return 0;
2439                    }
2440                }
2441            }
2442
2443            Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
2444            return -1;
2445        }
2446
2447        public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
2448            if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
2449
2450            PackageInfo app = null;
2451            try {
2452                app = mPackageManager.getPackageInfo(packageName, 0);
2453            } catch (NameNotFoundException nnf) {
2454                Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
2455                return -1;
2456            }
2457
2458            // If the caller is not privileged and is not coming from the target
2459            // app's uid, throw a permission exception back to the caller.
2460            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
2461                    Binder.getCallingPid(), Binder.getCallingUid());
2462            if ((perm == PackageManager.PERMISSION_DENIED) &&
2463                    (app.applicationInfo.uid != Binder.getCallingUid())) {
2464                Slog.w(TAG, "restorePackage: bad packageName=" + packageName
2465                        + " or calling uid=" + Binder.getCallingUid());
2466                throw new SecurityException("No permission to restore other packages");
2467            }
2468
2469            // If the package has no backup agent, we obviously cannot proceed
2470            if (app.applicationInfo.backupAgentName == null) {
2471                Slog.w(TAG, "Asked to restore package " + packageName + " with no agent");
2472                return -1;
2473            }
2474
2475            // So far so good; we're allowed to try to restore this package.  Now
2476            // check whether there is data for it in the current dataset, falling back
2477            // to the ancestral dataset if not.
2478            long token = getAvailableRestoreToken(packageName);
2479
2480            // If we didn't come up with a place to look -- no ancestral dataset and
2481            // the app has never been backed up from this device -- there's nothing
2482            // to do but return failure.
2483            if (token == 0) {
2484                return -1;
2485            }
2486
2487            // Ready to go:  enqueue the restore request and claim success
2488            long oldId = Binder.clearCallingIdentity();
2489            mWakelock.acquire();
2490            Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
2491            msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0);
2492            mBackupHandler.sendMessage(msg);
2493            Binder.restoreCallingIdentity(oldId);
2494            return 0;
2495        }
2496
2497        public synchronized void endRestoreSession() {
2498            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2499                    "endRestoreSession");
2500
2501            if (DEBUG) Slog.d(TAG, "endRestoreSession");
2502
2503            synchronized (this) {
2504                long oldId = Binder.clearCallingIdentity();
2505                try {
2506                    if (mRestoreTransport != null) mRestoreTransport.finishRestore();
2507                } catch (Exception e) {
2508                    Slog.e(TAG, "Error in finishRestore", e);
2509                } finally {
2510                    mRestoreTransport = null;
2511                    Binder.restoreCallingIdentity(oldId);
2512                }
2513            }
2514
2515            synchronized (BackupManagerService.this) {
2516                if (BackupManagerService.this.mActiveRestoreSession == this) {
2517                    BackupManagerService.this.mActiveRestoreSession = null;
2518                } else {
2519                    Slog.e(TAG, "ending non-current restore session");
2520                }
2521            }
2522        }
2523    }
2524
2525
2526    @Override
2527    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2528        synchronized (mQueueLock) {
2529            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
2530                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
2531                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
2532            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
2533            pw.println("Last backup pass: " + mLastBackupPass
2534                    + " (now = " + System.currentTimeMillis() + ')');
2535            pw.println("  next scheduled: " + mNextBackupPass);
2536
2537            pw.println("Available transports:");
2538            for (String t : listAllTransports()) {
2539                pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
2540                try {
2541                    File dir = new File(mBaseStateDir, getTransport(t).transportDirName());
2542                    for (File f : dir.listFiles()) {
2543                        pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
2544                    }
2545                } catch (RemoteException e) {
2546                    Slog.e(TAG, "Error in transportDirName()", e);
2547                    pw.println("        Error: " + e);
2548                }
2549            }
2550
2551            pw.println("Pending init: " + mPendingInits.size());
2552            for (String s : mPendingInits) {
2553                pw.println("    " + s);
2554            }
2555
2556            int N = mBackupParticipants.size();
2557            pw.println("Participants:");
2558            for (int i=0; i<N; i++) {
2559                int uid = mBackupParticipants.keyAt(i);
2560                pw.print("  uid: ");
2561                pw.println(uid);
2562                HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
2563                for (ApplicationInfo app: participants) {
2564                    pw.println("    " + app.packageName);
2565                }
2566            }
2567
2568            pw.println("Ancestral packages: "
2569                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
2570            if (mAncestralPackages != null) {
2571                for (String pkg : mAncestralPackages) {
2572                    pw.println("    " + pkg);
2573                }
2574            }
2575
2576            pw.println("Ever backed up: " + mEverStoredApps.size());
2577            for (String pkg : mEverStoredApps) {
2578                pw.println("    " + pkg);
2579            }
2580
2581            pw.println("Pending backup: " + mPendingBackups.size());
2582            for (BackupRequest req : mPendingBackups.values()) {
2583                pw.println("    " + req);
2584            }
2585        }
2586    }
2587}
2588