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