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