BackupManagerService.java revision 5e1ab335e6e8fbfa19c64d53880a22f472010953
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.AlarmManager;
21import android.app.IActivityManager;
22import android.app.IApplicationThread;
23import android.app.IBackupAgent;
24import android.app.PendingIntent;
25import android.content.BroadcastReceiver;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.ServiceConnection;
31import android.content.pm.ApplicationInfo;
32import android.content.pm.IPackageDataObserver;
33import android.content.pm.PackageInfo;
34import android.content.pm.PermissionInfo;
35import android.content.pm.PackageManager.NameNotFoundException;
36import android.content.pm.PackageManager;
37import android.content.pm.Signature;
38import android.net.Uri;
39import android.provider.Settings;
40import android.os.Binder;
41import android.os.Bundle;
42import android.os.Environment;
43import android.os.Handler;
44import android.os.IBinder;
45import android.os.Message;
46import android.os.ParcelFileDescriptor;
47import android.os.PowerManager;
48import android.os.Process;
49import android.os.RemoteException;
50import android.os.SystemClock;
51import android.util.EventLog;
52import android.util.Log;
53import android.util.SparseArray;
54
55import android.backup.IBackupManager;
56import android.backup.IRestoreObserver;
57import android.backup.IRestoreSession;
58import android.backup.RestoreSet;
59
60import com.android.internal.backup.LocalTransport;
61import com.android.internal.backup.IBackupTransport;
62
63import com.android.server.PackageManagerBackupAgent;
64import com.android.server.PackageManagerBackupAgent.Metadata;
65
66import java.io.EOFException;
67import java.io.File;
68import java.io.FileDescriptor;
69import java.io.IOException;
70import java.io.PrintWriter;
71import java.io.RandomAccessFile;
72import java.lang.String;
73import java.util.ArrayList;
74import java.util.HashMap;
75import java.util.HashSet;
76import java.util.List;
77import java.util.Map;
78
79class BackupManagerService extends IBackupManager.Stub {
80    private static final String TAG = "BackupManagerService";
81    private static final boolean DEBUG = true;
82
83    // How often we perform a backup pass.  Privileged external callers can
84    // trigger an immediate pass.
85    private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
86
87    // The amount of time between the initial provisioning of the device and
88    // the first backup pass.
89    private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
90
91    private static final String RUN_BACKUP_ACTION = "_backup_run_";
92    private static final int MSG_RUN_BACKUP = 1;
93    private static final int MSG_RUN_FULL_BACKUP = 2;
94    private static final int MSG_RUN_RESTORE = 3;
95    private static final int MSG_RUN_CLEAR = 4;
96
97    // Event tags -- see system/core/logcat/event-log-tags
98    private static final int BACKUP_DATA_CHANGED_EVENT = 2820;
99    private static final int BACKUP_START_EVENT = 2821;
100    private static final int BACKUP_TRANSPORT_FAILURE_EVENT = 2822;
101    private static final int BACKUP_AGENT_FAILURE_EVENT = 2823;
102    private static final int BACKUP_PACKAGE_EVENT = 2824;
103    private static final int BACKUP_SUCCESS_EVENT = 2825;
104
105    private static final int RESTORE_START_EVENT = 2830;
106    private static final int RESTORE_TRANSPORT_FAILURE_EVENT = 2831;
107    private static final int RESTORE_AGENT_FAILURE_EVENT = 2832;
108    private static final int RESTORE_PACKAGE_EVENT = 2833;
109    private static final int RESTORE_SUCCESS_EVENT = 2834;
110
111    // Timeout interval for deciding that a bind or clear-data has taken too long
112    static final long TIMEOUT_INTERVAL = 10 * 1000;
113
114    private Context mContext;
115    private PackageManager mPackageManager;
116    private IActivityManager mActivityManager;
117    private PowerManager mPowerManager;
118    private AlarmManager mAlarmManager;
119
120    boolean mEnabled;   // access to this is synchronized on 'this'
121    boolean mProvisioned;
122    PowerManager.WakeLock mWakelock;
123    final BackupHandler mBackupHandler = new BackupHandler();
124    PendingIntent mRunBackupIntent;
125    BroadcastReceiver mRunBackupReceiver;
126    IntentFilter mRunBackupFilter;
127    // map UIDs to the set of backup client services within that UID's app set
128    final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
129        = new SparseArray<HashSet<ApplicationInfo>>();
130    // set of backup services that have pending changes
131    class BackupRequest {
132        public ApplicationInfo appInfo;
133        public boolean fullBackup;
134
135        BackupRequest(ApplicationInfo app, boolean isFull) {
136            appInfo = app;
137            fullBackup = isFull;
138        }
139
140        public String toString() {
141            return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
142        }
143    }
144    // Backups that we haven't started yet.
145    HashMap<ApplicationInfo,BackupRequest> mPendingBackups
146            = new HashMap<ApplicationInfo,BackupRequest>();
147
148    // Pseudoname that we use for the Package Manager metadata "package"
149    static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
150
151    // locking around the pending-backup management
152    final Object mQueueLock = new Object();
153
154    // The thread performing the sequence of queued backups binds to each app's agent
155    // in succession.  Bind notifications are asynchronously delivered through the
156    // Activity Manager; use this lock object to signal when a requested binding has
157    // completed.
158    final Object mAgentConnectLock = new Object();
159    IBackupAgent mConnectedAgent;
160    volatile boolean mConnecting;
161
162    // A similar synchronicity mechanism around clearing apps' data for restore
163    final Object mClearDataLock = new Object();
164    volatile boolean mClearingData;
165
166    // Transport bookkeeping
167    final HashMap<String,IBackupTransport> mTransports
168            = new HashMap<String,IBackupTransport>();
169    String mCurrentTransport;
170    IBackupTransport mLocalTransport, mGoogleTransport;
171    RestoreSession mActiveRestoreSession;
172
173    class RestoreParams {
174        public IBackupTransport transport;
175        public IRestoreObserver observer;
176        public long token;
177
178        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
179            transport = _transport;
180            observer = _obs;
181            token = _token;
182        }
183    }
184
185    class ClearParams {
186        public IBackupTransport transport;
187        public PackageInfo packageInfo;
188
189        ClearParams(IBackupTransport _transport, PackageInfo _info) {
190            transport = _transport;
191            packageInfo = _info;
192        }
193    }
194
195    // Where we keep our journal files and other bookkeeping
196    File mBaseStateDir;
197    File mDataDir;
198    File mJournalDir;
199    File mJournal;
200    RandomAccessFile mJournalStream;
201
202    // Keep a log of all the apps we've ever backed up
203    private File mEverStored;
204    private RandomAccessFile mEverStoredStream;
205    HashSet<String> mEverStoredApps = new HashSet<String>();
206
207
208    public BackupManagerService(Context context) {
209        mContext = context;
210        mPackageManager = context.getPackageManager();
211        mActivityManager = ActivityManagerNative.getDefault();
212
213        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
214        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
215
216        // Set up our bookkeeping
217        boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
218                Settings.Secure.BACKUP_ENABLED, 0) != 0;
219        // !!! TODO: mProvisioned needs to default to 0, not 1.
220        mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
221                Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
222        mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
223        mDataDir = Environment.getDownloadCacheDirectory();
224
225        mRunBackupReceiver = new RunBackupReceiver();
226        mRunBackupFilter = new IntentFilter();
227        mRunBackupFilter.addAction(RUN_BACKUP_ACTION);
228        context.registerReceiver(mRunBackupReceiver, mRunBackupFilter);
229
230        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
231        // !!! TODO: restrict delivery to our receiver; the naive setClass() doesn't seem to work
232        //backupIntent.setClass(context, mRunBackupReceiver.getClass());
233        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
234        mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
235
236        // Set up the backup-request journaling
237        mJournalDir = new File(mBaseStateDir, "pending");
238        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
239        makeJournalLocked();    // okay because no other threads are running yet
240
241        // Set up the various sorts of package tracking we do
242        initPackageTracking();
243
244        // Build our mapping of uid to backup client services.  This implicitly
245        // schedules a backup pass on the Package Manager metadata the first
246        // time anything needs to be backed up.
247        synchronized (mBackupParticipants) {
248            addPackageParticipantsLocked(null);
249        }
250
251        // Set up our transport options and initialize the default transport
252        // TODO: Have transports register themselves somehow?
253        // TODO: Don't create transports that we don't need to?
254        mLocalTransport = new LocalTransport(context);  // This is actually pretty cheap
255        ComponentName localName = new ComponentName(context, LocalTransport.class);
256        registerTransport(localName.flattenToShortString(), mLocalTransport);
257
258        mGoogleTransport = null;
259        mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
260                Settings.Secure.BACKUP_TRANSPORT);
261        if ("".equals(mCurrentTransport)) {
262            mCurrentTransport = null;
263        }
264        if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport);
265
266        // Attach to the Google backup transport.  When this comes up, it will set
267        // itself as the current transport because we explicitly reset mCurrentTransport
268        // to null.
269        Intent intent = new Intent().setComponent(new ComponentName(
270                "com.google.android.backup",
271                "com.google.android.backup.BackupTransportService"));
272        context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
273
274        // Now that we know about valid backup participants, parse any
275        // leftover journal files into the pending backup set
276        parseLeftoverJournals();
277
278        // Power management
279        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
280
281        // Start the backup passes going
282        setBackupEnabled(areEnabled);
283    }
284
285    private class RunBackupReceiver extends BroadcastReceiver {
286        public void onReceive(Context context, Intent intent) {
287            if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
288                if (DEBUG) Log.v(TAG, "Running a backup pass");
289
290                synchronized (mQueueLock) {
291                    // acquire a wakelock and pass it to the backup thread.  it will
292                    // be released once backup concludes.
293                    mWakelock.acquire();
294
295                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
296                    mBackupHandler.sendMessage(msg);
297                }
298            }
299        }
300    }
301
302    private void initPackageTracking() {
303        if (DEBUG) Log.v(TAG, "Initializing package tracking");
304
305        // Keep a log of what apps we've ever backed up.  Because we might have
306        // rebooted in the middle of an operation that was removing something from
307        // this log, we sanity-check its contents here and reconstruct it.
308        mEverStored = new File(mBaseStateDir, "processed");
309        File tempProcessedFile = new File(mBaseStateDir, "processed.new");
310        try {
311            // If there are previous contents, parse them out then start a new
312            // file to continue the recordkeeping.
313            if (mEverStored.exists()) {
314                RandomAccessFile temp = new RandomAccessFile(tempProcessedFile, "rw");
315                mEverStoredStream = new RandomAccessFile(mEverStored, "r");
316
317                // parse its existing contents
318                mEverStoredStream.seek(0);
319                temp.seek(0);
320                try {
321                    while (true) {
322                        PackageInfo info;
323                        String pkg = mEverStoredStream.readUTF();
324                        try {
325                            info = mPackageManager.getPackageInfo(pkg, 0);
326                            mEverStoredApps.add(pkg);
327                            temp.writeUTF(pkg);
328                            if (DEBUG) Log.v(TAG, "   + " + pkg);
329                        } catch (NameNotFoundException e) {
330                            // nope, this package was uninstalled; don't include it
331                            if (DEBUG) Log.v(TAG, "   - " + pkg);
332                        }
333                    }
334                } catch (EOFException e) {
335                    // now we're at EOF
336                }
337
338                // Once we've rewritten the backup history log, atomically replace the
339                // old one with the new one then reopen the file for continuing use.
340                temp.close();
341                mEverStoredStream.close();
342                tempProcessedFile.renameTo(mEverStored);
343            }
344            // This will create the file if it doesn't exist
345            mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");
346            mEverStoredStream.seek(mEverStoredStream.length());
347        } catch (IOException e) {
348            Log.e(TAG, "Unable to open known-stored file!");
349            mEverStoredStream = null;
350        }
351
352        // If we were in the middle of removing something from the ever-backed-up
353        // file, there might be a transient "processed.new" file still present.
354        // We've reconstructed a coherent state at this point though, so we can
355        // safely discard that file now.
356        if (tempProcessedFile.exists()) {
357            tempProcessedFile.delete();
358        }
359
360        // Register for broadcasts about package install, etc., so we can
361        // update the provider list.
362        IntentFilter filter = new IntentFilter();
363        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
364        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
365        filter.addDataScheme("package");
366        mContext.registerReceiver(mBroadcastReceiver, filter);
367    }
368
369    private void makeJournalLocked() {
370        try {
371            mJournal = File.createTempFile("journal", null, mJournalDir);
372            mJournalStream = new RandomAccessFile(mJournal, "rwd");
373        } catch (IOException e) {
374            Log.e(TAG, "Unable to write backup journals");
375            mJournal = null;
376            mJournalStream = null;
377        }
378    }
379
380    private void parseLeftoverJournals() {
381        if (mJournal != null) {
382            File[] allJournals = mJournalDir.listFiles();
383            for (File f : allJournals) {
384                if (f.compareTo(mJournal) != 0) {
385                    // This isn't the current journal, so it must be a leftover.  Read
386                    // out the package names mentioned there and schedule them for
387                    // backup.
388                    try {
389                        Log.i(TAG, "Found stale backup journal, scheduling:");
390                        RandomAccessFile in = new RandomAccessFile(f, "r");
391                        while (true) {
392                            String packageName = in.readUTF();
393                            Log.i(TAG, "    + " + packageName);
394                            dataChanged(packageName);
395                        }
396                    } catch (EOFException e) {
397                        // no more data; we're done
398                    } catch (Exception e) {
399                        // can't read it or other error; just skip it
400                    } finally {
401                        // close/delete the file
402                        f.delete();
403                    }
404                }
405            }
406        }
407    }
408
409    // Add a transport to our set of available backends
410    private void registerTransport(String name, IBackupTransport transport) {
411        synchronized (mTransports) {
412            if (DEBUG) Log.v(TAG, "Registering transport " + name + " = " + transport);
413            mTransports.put(name, transport);
414        }
415    }
416
417    // ----- Track installation/removal of packages -----
418    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
419        public void onReceive(Context context, Intent intent) {
420            if (DEBUG) Log.d(TAG, "Received broadcast " + intent);
421
422            Uri uri = intent.getData();
423            if (uri == null) {
424                return;
425            }
426            String pkgName = uri.getSchemeSpecificPart();
427            if (pkgName == null) {
428                return;
429            }
430
431            String action = intent.getAction();
432            if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
433                synchronized (mBackupParticipants) {
434                    Bundle extras = intent.getExtras();
435                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
436                        // The package was just upgraded
437                        updatePackageParticipantsLocked(pkgName);
438                    } else {
439                        // The package was just added
440                        addPackageParticipantsLocked(pkgName);
441                    }
442                }
443            }
444            else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
445                Bundle extras = intent.getExtras();
446                if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
447                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
448                } else {
449                    synchronized (mBackupParticipants) {
450                        removePackageParticipantsLocked(pkgName);
451                    }
452                }
453            }
454        }
455    };
456
457    // ----- Track connection to GoogleBackupTransport service -----
458    ServiceConnection mGoogleConnection = new ServiceConnection() {
459        public void onServiceConnected(ComponentName name, IBinder service) {
460            if (DEBUG) Log.v(TAG, "Connected to Google transport");
461            mGoogleTransport = IBackupTransport.Stub.asInterface(service);
462            registerTransport(name.flattenToShortString(), mGoogleTransport);
463        }
464
465        public void onServiceDisconnected(ComponentName name) {
466            if (DEBUG) Log.v(TAG, "Disconnected from Google transport");
467            mGoogleTransport = null;
468            registerTransport(name.flattenToShortString(), null);
469        }
470    };
471
472    // ----- Run the actual backup process asynchronously -----
473
474    private class BackupHandler extends Handler {
475        public void handleMessage(Message msg) {
476
477            switch (msg.what) {
478            case MSG_RUN_BACKUP:
479            {
480                IBackupTransport transport = getTransport(mCurrentTransport);
481                if (transport == null) {
482                    Log.v(TAG, "Backup requested but no transport available");
483                    mWakelock.release();
484                    break;
485                }
486
487                // snapshot the pending-backup set and work on that
488                ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
489                File oldJournal = mJournal;
490                synchronized (mQueueLock) {
491                    // Do we have any work to do?
492                    if (mPendingBackups.size() > 0) {
493                        for (BackupRequest b: mPendingBackups.values()) {
494                            queue.add(b);
495                        }
496                        Log.v(TAG, "clearing pending backups");
497                        mPendingBackups.clear();
498
499                        // Start a new backup-queue journal file too
500                        if (mJournalStream != null) {
501                            try {
502                                mJournalStream.close();
503                            } catch (IOException e) {
504                                // don't need to do anything
505                            }
506                            makeJournalLocked();
507                        }
508
509                        // At this point, we have started a new journal file, and the old
510                        // file identity is being passed to the backup processing thread.
511                        // When it completes successfully, that old journal file will be
512                        // deleted.  If we crash prior to that, the old journal is parsed
513                        // at next boot and the journaled requests fulfilled.
514                        (new PerformBackupThread(transport, queue, oldJournal)).start();
515                    } else {
516                        Log.v(TAG, "Backup requested but nothing pending");
517                        mWakelock.release();
518                    }
519                }
520                break;
521            }
522
523            case MSG_RUN_FULL_BACKUP:
524                break;
525
526            case MSG_RUN_RESTORE:
527            {
528                RestoreParams params = (RestoreParams)msg.obj;
529                Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
530                (new PerformRestoreThread(params.transport, params.observer,
531                        params.token)).start();
532                break;
533            }
534
535            case MSG_RUN_CLEAR:
536            {
537                ClearParams params = (ClearParams)msg.obj;
538                (new PerformClearThread(params.transport, params.packageInfo)).start();
539                break;
540            }
541            }
542        }
543    }
544
545    // Add the backup agents in the given package to our set of known backup participants.
546    // If 'packageName' is null, adds all backup agents in the whole system.
547    void addPackageParticipantsLocked(String packageName) {
548        // Look for apps that define the android:backupAgent attribute
549        if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName);
550        List<PackageInfo> targetApps = allAgentPackages();
551        addPackageParticipantsLockedInner(packageName, targetApps);
552    }
553
554    private void addPackageParticipantsLockedInner(String packageName,
555            List<PackageInfo> targetPkgs) {
556        if (DEBUG) {
557            Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
558            for (PackageInfo p : targetPkgs) {
559                Log.v(TAG, "    " + p + " agent=" + p.applicationInfo.backupAgentName
560                        + " uid=" + p.applicationInfo.uid
561                        + " killAfterRestore="
562                        + (((p.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) ? "true" : "false")
563                        + " restoreNeedsApplication="
564                        + (((p.applicationInfo.flags & ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION) != 0) ? "true" : "false")
565                        );
566            }
567        }
568
569        for (PackageInfo pkg : targetPkgs) {
570            if (packageName == null || pkg.packageName.equals(packageName)) {
571                int uid = pkg.applicationInfo.uid;
572                HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
573                if (set == null) {
574                    set = new HashSet<ApplicationInfo>();
575                    mBackupParticipants.put(uid, set);
576                }
577                set.add(pkg.applicationInfo);
578
579                // If we've never seen this app before, schedule a backup for it
580                if (!mEverStoredApps.contains(pkg.packageName)) {
581                    if (DEBUG) Log.i(TAG, "New app " + pkg.packageName
582                            + " never backed up; scheduling");
583                    try {
584                        dataChanged(pkg.packageName);
585                    } catch (RemoteException e) {
586                        // can't happen; it's a local method call
587                    }
588                }
589            }
590        }
591    }
592
593    // Remove the given package's entry from our known active set.  If
594    // 'packageName' is null, *all* participating apps will be removed.
595    void removePackageParticipantsLocked(String packageName) {
596        if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
597        List<PackageInfo> allApps = null;
598        if (packageName != null) {
599            allApps = new ArrayList<PackageInfo>();
600            try {
601                int flags = PackageManager.GET_SIGNATURES;
602                allApps.add(mPackageManager.getPackageInfo(packageName, flags));
603            } catch (Exception e) {
604                // just skip it (???)
605            }
606        } else {
607            // all apps with agents
608            allApps = allAgentPackages();
609        }
610        removePackageParticipantsLockedInner(packageName, allApps);
611    }
612
613    private void removePackageParticipantsLockedInner(String packageName,
614            List<PackageInfo> agents) {
615        if (DEBUG) {
616            Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName
617                    + ") removing " + agents.size() + " entries");
618            for (PackageInfo p : agents) {
619                Log.v(TAG, "    - " + p);
620            }
621        }
622        for (PackageInfo pkg : agents) {
623            if (packageName == null || pkg.packageName.equals(packageName)) {
624                int uid = pkg.applicationInfo.uid;
625                HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
626                if (set != null) {
627                    // Find the existing entry with the same package name, and remove it.
628                    // We can't just remove(app) because the instances are different.
629                    for (ApplicationInfo entry: set) {
630                        if (entry.packageName.equals(pkg.packageName)) {
631                            set.remove(entry);
632                            removeEverBackedUp(pkg.packageName);
633                            break;
634                        }
635                    }
636                    if (set.size() == 0) {
637                        mBackupParticipants.delete(uid);
638                    }
639                }
640            }
641        }
642    }
643
644    // Returns the set of all applications that define an android:backupAgent attribute
645    List<PackageInfo> allAgentPackages() {
646        // !!! TODO: cache this and regenerate only when necessary
647        int flags = PackageManager.GET_SIGNATURES;
648        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
649        int N = packages.size();
650        for (int a = N-1; a >= 0; a--) {
651            PackageInfo pkg = packages.get(a);
652            ApplicationInfo app = pkg.applicationInfo;
653            if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
654                    || app.backupAgentName == null
655                    || (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA,
656                            pkg.packageName) != PackageManager.PERMISSION_GRANTED)) {
657                packages.remove(a);
658            }
659        }
660        return packages;
661    }
662
663    // Reset the given package's known backup participants.  Unlike add/remove, the update
664    // action cannot be passed a null package name.
665    void updatePackageParticipantsLocked(String packageName) {
666        if (packageName == null) {
667            Log.e(TAG, "updatePackageParticipants called with null package name");
668            return;
669        }
670        if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName);
671
672        // brute force but small code size
673        List<PackageInfo> allApps = allAgentPackages();
674        removePackageParticipantsLockedInner(packageName, allApps);
675        addPackageParticipantsLockedInner(packageName, allApps);
676    }
677
678    // Called from the backup thread: record that the given app has been successfully
679    // backed up at least once
680    void logBackupComplete(String packageName) {
681        if (mEverStoredStream != null && !packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
682            synchronized (mEverStoredApps) {
683                if (mEverStoredApps.add(packageName)) {
684                    try {
685                        mEverStoredStream.writeUTF(packageName);
686                    } catch (IOException e) {
687                        Log.e(TAG, "Unable to log backup of " + packageName + ", ceasing log");
688                        try {
689                            mEverStoredStream.close();
690                        } catch (IOException ioe) {
691                            // we're dropping it; no need to handle an exception on close here
692                        }
693                        mEverStoredStream = null;
694                    }
695                }
696            }
697        }
698    }
699
700    // Remove our awareness of having ever backed up the given package
701    void removeEverBackedUp(String packageName) {
702        if (DEBUG) Log.v(TAG, "Removing backed-up knowledge of " + packageName
703                + ", new set:");
704
705        if (mEverStoredStream != null) {
706            synchronized (mEverStoredApps) {
707                // Rewrite the file and rename to overwrite.  If we reboot in the middle,
708                // we'll recognize on initialization time that the package no longer
709                // exists and fix it up then.
710                File tempKnownFile = new File(mBaseStateDir, "processed.new");
711                try {
712                    mEverStoredStream.close();
713                    RandomAccessFile known = new RandomAccessFile(tempKnownFile, "rw");
714                    mEverStoredApps.remove(packageName);
715                    for (String s : mEverStoredApps) {
716                        known.writeUTF(s);
717                        if (DEBUG) Log.v(TAG, "    " + s);
718                    }
719                    known.close();
720                    tempKnownFile.renameTo(mEverStored);
721                    mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");
722                } catch (IOException e) {
723                    // Bad: we couldn't create the new copy.  For safety's sake we
724                    // abandon the whole process and remove all what's-backed-up
725                    // state entirely, meaning we'll force a backup pass for every
726                    // participant on the next boot or [re]install.
727                    Log.w(TAG, "Error rewriting backed-up set; halting log");
728                    mEverStoredStream = null;
729                    mEverStoredApps.clear();
730                    tempKnownFile.delete();
731                    mEverStored.delete();
732                }
733            }
734        }
735    }
736
737    // Return the given transport
738    private IBackupTransport getTransport(String transportName) {
739        synchronized (mTransports) {
740            IBackupTransport transport = mTransports.get(transportName);
741            if (transport == null) {
742                Log.w(TAG, "Requested unavailable transport: " + transportName);
743            }
744            return transport;
745        }
746    }
747
748    // fire off a backup agent, blocking until it attaches or times out
749    IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
750        IBackupAgent agent = null;
751        synchronized(mAgentConnectLock) {
752            mConnecting = true;
753            mConnectedAgent = null;
754            try {
755                if (mActivityManager.bindBackupAgent(app, mode)) {
756                    Log.d(TAG, "awaiting agent for " + app);
757
758                    // success; wait for the agent to arrive
759                    // only wait 10 seconds for the clear data to happen
760                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
761                    while (mConnecting && mConnectedAgent == null
762                            && (System.currentTimeMillis() < timeoutMark)) {
763                        try {
764                            mAgentConnectLock.wait(5000);
765                        } catch (InterruptedException e) {
766                            // just bail
767                            return null;
768                        }
769                    }
770
771                    // if we timed out with no connect, abort and move on
772                    if (mConnecting == true) {
773                        Log.w(TAG, "Timeout waiting for agent " + app);
774                        return null;
775                    }
776                    agent = mConnectedAgent;
777                }
778            } catch (RemoteException e) {
779                // can't happen
780            }
781        }
782        return agent;
783    }
784
785    // clear an application's data, blocking until the operation completes or times out
786    void clearApplicationDataSynchronous(String packageName) {
787        // Don't wipe packages marked allowClearUserData=false
788        try {
789            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
790            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
791                if (DEBUG) Log.i(TAG, "allowClearUserData=false so not wiping "
792                        + packageName);
793                return;
794            }
795        } catch (NameNotFoundException e) {
796            Log.w(TAG, "Tried to clear data for " + packageName + " but not found");
797            return;
798        }
799
800        ClearDataObserver observer = new ClearDataObserver();
801
802        synchronized(mClearDataLock) {
803            mClearingData = true;
804            /* This is causing some critical processes to be killed during setup.
805               Temporarily revert this change until we find a better solution.
806            try {
807                mActivityManager.clearApplicationUserData(packageName, observer);
808            } catch (RemoteException e) {
809                // can't happen because the activity manager is in this process
810            }
811            */
812            mPackageManager.clearApplicationUserData(packageName, observer);
813
814            // only wait 10 seconds for the clear data to happen
815            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
816            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
817                try {
818                    mClearDataLock.wait(5000);
819                } catch (InterruptedException e) {
820                    // won't happen, but still.
821                    mClearingData = false;
822                }
823            }
824        }
825    }
826
827    class ClearDataObserver extends IPackageDataObserver.Stub {
828        public void onRemoveCompleted(String packageName, boolean succeeded)
829                throws RemoteException {
830            synchronized(mClearDataLock) {
831                mClearingData = false;
832                mClearDataLock.notifyAll();
833            }
834        }
835    }
836
837    // ----- Back up a set of applications via a worker thread -----
838
839    class PerformBackupThread extends Thread {
840        private static final String TAG = "PerformBackupThread";
841        IBackupTransport mTransport;
842        ArrayList<BackupRequest> mQueue;
843        File mStateDir;
844        File mJournal;
845
846        public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue,
847                File journal) {
848            mTransport = transport;
849            mQueue = queue;
850            mJournal = journal;
851
852            try {
853                mStateDir = new File(mBaseStateDir, transport.transportDirName());
854            } catch (RemoteException e) {
855                // can't happen; the transport is local
856            }
857            mStateDir.mkdirs();
858        }
859
860        @Override
861        public void run() {
862            long startRealtime = SystemClock.elapsedRealtime();
863            if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
864
865            // Backups run at background priority
866            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
867
868            try {
869                EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName());
870
871                // The package manager doesn't have a proper <application> etc, but since
872                // it's running here in the system process we can just set up its agent
873                // directly and use a synthetic BackupRequest.  We always run this pass
874                // because it's cheap and this way we guarantee that we don't get out of
875                // step even if we're selecting among various transports at run time.
876                PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
877                        mPackageManager, allAgentPackages());
878                BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
879                pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
880                processOneBackup(pmRequest,
881                        IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
882
883                // Now run all the backups in our queue
884                int count = mQueue.size();
885                doQueuedBackups(mTransport);
886
887                // Finally, tear down the transport
888                if (mTransport.finishBackup()) {
889                    int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
890                    EventLog.writeEvent(BACKUP_SUCCESS_EVENT, count, millis);
891                } else {
892                    EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "");
893                    Log.e(TAG, "Transport error in finishBackup()");
894                }
895
896                if (!mJournal.delete()) {
897                    Log.e(TAG, "Unable to remove backup journal file " + mJournal);
898                }
899            } catch (Exception e) {
900                Log.e(TAG, "Error in backup thread", e);
901            } finally {
902                // Only once we're entirely finished do we release the wakelock
903                mWakelock.release();
904            }
905        }
906
907        private void doQueuedBackups(IBackupTransport transport) {
908            for (BackupRequest request : mQueue) {
909                Log.d(TAG, "starting agent for backup of " + request);
910
911                // Don't run backup, even if requested, if the target app does not have
912                // the requisite permission
913                if (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA,
914                        request.appInfo.packageName) != PackageManager.PERMISSION_GRANTED) {
915                    Log.w(TAG, "Skipping backup of unprivileged package "
916                            + request.appInfo.packageName);
917                    continue;
918                }
919
920                IBackupAgent agent = null;
921                int mode = (request.fullBackup)
922                        ? IApplicationThread.BACKUP_MODE_FULL
923                        : IApplicationThread.BACKUP_MODE_INCREMENTAL;
924                try {
925                    agent = bindToAgentSynchronous(request.appInfo, mode);
926                    if (agent != null) {
927                        processOneBackup(request, agent, transport);
928                    }
929
930                    // unbind even on timeout, just in case
931                    mActivityManager.unbindBackupAgent(request.appInfo);
932                } catch (SecurityException ex) {
933                    // Try for the next one.
934                    Log.d(TAG, "error in bind/backup", ex);
935                } catch (RemoteException e) {
936                    Log.v(TAG, "bind/backup threw");
937                    e.printStackTrace();
938                }
939            }
940        }
941
942        void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
943            final String packageName = request.appInfo.packageName;
944            if (DEBUG) Log.d(TAG, "processOneBackup doBackup() on " + packageName);
945
946            // !!! TODO: get the state file dir from the transport
947            File savedStateName = new File(mStateDir, packageName);
948            File backupDataName = new File(mDataDir, packageName + ".data");
949            File newStateName = new File(mStateDir, packageName + ".new");
950
951            ParcelFileDescriptor savedState = null;
952            ParcelFileDescriptor backupData = null;
953            ParcelFileDescriptor newState = null;
954
955            PackageInfo packInfo;
956            try {
957                // Look up the package info & signatures.  This is first so that if it
958                // throws an exception, there's no file setup yet that would need to
959                // be unraveled.
960                if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
961                    // The metadata 'package' is synthetic
962                    packInfo = new PackageInfo();
963                    packInfo.packageName = packageName;
964                } else {
965                    packInfo = mPackageManager.getPackageInfo(packageName,
966                        PackageManager.GET_SIGNATURES);
967                }
968
969                // In a full backup, we pass a null ParcelFileDescriptor as
970                // the saved-state "file"
971                if (!request.fullBackup) {
972                    savedState = ParcelFileDescriptor.open(savedStateName,
973                            ParcelFileDescriptor.MODE_READ_ONLY |
974                            ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
975                }
976
977                backupData = ParcelFileDescriptor.open(backupDataName,
978                        ParcelFileDescriptor.MODE_READ_WRITE |
979                        ParcelFileDescriptor.MODE_CREATE |
980                        ParcelFileDescriptor.MODE_TRUNCATE);
981
982                newState = ParcelFileDescriptor.open(newStateName,
983                        ParcelFileDescriptor.MODE_READ_WRITE |
984                        ParcelFileDescriptor.MODE_CREATE |
985                        ParcelFileDescriptor.MODE_TRUNCATE);
986
987                // Run the target's backup pass
988                agent.doBackup(savedState, backupData, newState);
989                logBackupComplete(packageName);
990                if (DEBUG) Log.v(TAG, "doBackup() success");
991            } catch (Exception e) {
992                Log.e(TAG, "Error backing up " + packageName, e);
993                EventLog.writeEvent(BACKUP_AGENT_FAILURE_EVENT, packageName, e.toString());
994                backupDataName.delete();
995                newStateName.delete();
996                return;
997            } finally {
998                try { if (savedState != null) savedState.close(); } catch (IOException e) {}
999                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1000                try { if (newState != null) newState.close(); } catch (IOException e) {}
1001                savedState = backupData = newState = null;
1002            }
1003
1004            // Now propagate the newly-backed-up data to the transport
1005            try {
1006                int size = (int) backupDataName.length();
1007                if (size > 0) {
1008                    backupData = ParcelFileDescriptor.open(backupDataName,
1009                            ParcelFileDescriptor.MODE_READ_ONLY);
1010
1011                    if (!transport.performBackup(packInfo, backupData)) throw new Exception();
1012                } else {
1013                    if (DEBUG) Log.i(TAG, "no backup data written; not calling transport");
1014                }
1015
1016                // After successful transport, delete the now-stale data
1017                // and juggle the files so that next time we supply the agent
1018                // with the new state file it just created.
1019                backupDataName.delete();
1020                newStateName.renameTo(savedStateName);
1021                EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size);
1022            } catch (Exception e) {
1023                Log.e(TAG, "Transport error backing up " + packageName, e);
1024                EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
1025                return;
1026            } finally {
1027                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1028            }
1029        }
1030    }
1031
1032
1033    // ----- Restore handling -----
1034
1035    private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
1036        // Allow unsigned apps, but not signed on one device and unsigned on the other
1037        // !!! TODO: is this the right policy?
1038        if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs
1039                + " device=" + deviceSigs);
1040        if ((storedSigs == null || storedSigs.length == 0)
1041                && (deviceSigs == null || deviceSigs.length == 0)) {
1042            return true;
1043        }
1044        if (storedSigs == null || deviceSigs == null) {
1045            return false;
1046        }
1047
1048        // !!! TODO: this demands that every stored signature match one
1049        // that is present on device, and does not demand the converse.
1050        // Is this this right policy?
1051        int nStored = storedSigs.length;
1052        int nDevice = deviceSigs.length;
1053
1054        for (int i=0; i < nStored; i++) {
1055            boolean match = false;
1056            for (int j=0; j < nDevice; j++) {
1057                if (storedSigs[i].equals(deviceSigs[j])) {
1058                    match = true;
1059                    break;
1060                }
1061            }
1062            if (!match) {
1063                return false;
1064            }
1065        }
1066        return true;
1067    }
1068
1069    class PerformRestoreThread extends Thread {
1070        private IBackupTransport mTransport;
1071        private IRestoreObserver mObserver;
1072        private long mToken;
1073        private File mStateDir;
1074
1075        class RestoreRequest {
1076            public PackageInfo app;
1077            public int storedAppVersion;
1078
1079            RestoreRequest(PackageInfo _app, int _version) {
1080                app = _app;
1081                storedAppVersion = _version;
1082            }
1083        }
1084
1085        PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
1086                long restoreSetToken) {
1087            mTransport = transport;
1088            Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver);
1089            mObserver = observer;
1090            mToken = restoreSetToken;
1091
1092            try {
1093                mStateDir = new File(mBaseStateDir, transport.transportDirName());
1094            } catch (RemoteException e) {
1095                // can't happen; the transport is local
1096            }
1097            mStateDir.mkdirs();
1098        }
1099
1100        @Override
1101        public void run() {
1102            long startRealtime = SystemClock.elapsedRealtime();
1103            if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
1104                    + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken));
1105            /**
1106             * Restore sequence:
1107             *
1108             * 1. get the restore set description for our identity
1109             * 2. for each app in the restore set:
1110             *    2.a. if it's restorable on this device, add it to the restore queue
1111             * 3. for each app in the restore queue:
1112             *    3.a. clear the app data
1113             *    3.b. get the restore data for the app from the transport
1114             *    3.c. launch the backup agent for the app
1115             *    3.d. agent.doRestore() with the data from the server
1116             *    3.e. unbind the agent [and kill the app?]
1117             * 4. shut down the transport
1118             *
1119             * On errors, we try our best to recover and move on to the next
1120             * application, but if necessary we abort the whole operation --
1121             * the user is waiting, after al.
1122             */
1123
1124            int error = -1; // assume error
1125
1126            // build the set of apps to restore
1127            try {
1128                // TODO: Log this before getAvailableRestoreSets, somehow
1129                EventLog.writeEvent(RESTORE_START_EVENT, mTransport.transportDirName());
1130
1131                // Get the list of all packages which have backup enabled.
1132                // (Include the Package Manager metadata pseudo-package first.)
1133                ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
1134                PackageInfo omPackage = new PackageInfo();
1135                omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
1136                restorePackages.add(omPackage);
1137
1138                List<PackageInfo> agentPackages = allAgentPackages();
1139                restorePackages.addAll(agentPackages);
1140
1141                // let the observer know that we're running
1142                if (mObserver != null) {
1143                    try {
1144                        // !!! TODO: get an actual count from the transport after
1145                        // its startRestore() runs?
1146                        mObserver.restoreStarting(restorePackages.size());
1147                    } catch (RemoteException e) {
1148                        Log.d(TAG, "Restore observer died at restoreStarting");
1149                        mObserver = null;
1150                    }
1151                }
1152
1153                if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) {
1154                    Log.e(TAG, "Error starting restore operation");
1155                    EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
1156                    return;
1157                }
1158
1159                String packageName = mTransport.nextRestorePackage();
1160                if (packageName == null) {
1161                    Log.e(TAG, "Error getting first restore package");
1162                    EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
1163                    return;
1164                } else if (packageName.equals("")) {
1165                    Log.i(TAG, "No restore data available");
1166                    int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
1167                    EventLog.writeEvent(RESTORE_SUCCESS_EVENT, 0, millis);
1168                    return;
1169                } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
1170                    Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
1171                          + "\", found only \"" + packageName + "\"");
1172                    EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL,
1173                            "Package manager data missing");
1174                    return;
1175                }
1176
1177                // Pull the Package Manager metadata from the restore set first
1178                PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
1179                        mPackageManager, agentPackages);
1180                processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
1181
1182                // Verify that the backup set includes metadata.  If not, we can't do
1183                // signature/version verification etc, so we simply do not proceed with
1184                // the restore operation.
1185                if (!pmAgent.hasMetadata()) {
1186                    Log.e(TAG, "No restore metadata available, so not restoring settings");
1187                    EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL,
1188                            "Package manager restore metadata missing");
1189                    return;
1190                }
1191
1192                int count = 0;
1193                for (;;) {
1194                    packageName = mTransport.nextRestorePackage();
1195
1196                    if (packageName == null) {
1197                        Log.e(TAG, "Error getting next restore package");
1198                        EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
1199                        return;
1200                    } else if (packageName.equals("")) {
1201                        break;
1202                    }
1203
1204                    if (mObserver != null) {
1205                        try {
1206                            mObserver.onUpdate(count);
1207                        } catch (RemoteException e) {
1208                            Log.d(TAG, "Restore observer died in onUpdate");
1209                            mObserver = null;
1210                        }
1211                    }
1212
1213                    Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
1214                    if (metaInfo == null) {
1215                        Log.e(TAG, "Missing metadata for " + packageName);
1216                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
1217                                "Package metadata missing");
1218                        continue;
1219                    }
1220
1221                    PackageInfo packageInfo;
1222                    try {
1223                        int flags = PackageManager.GET_SIGNATURES;
1224                        packageInfo = mPackageManager.getPackageInfo(packageName, flags);
1225                    } catch (NameNotFoundException e) {
1226                        Log.e(TAG, "Invalid package restoring data", e);
1227                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
1228                                "Package missing on device");
1229                        continue;
1230                    }
1231
1232                    if (metaInfo.versionCode > packageInfo.versionCode) {
1233                        String message = "Version " + metaInfo.versionCode
1234                                + " > installed version " + packageInfo.versionCode;
1235                        Log.w(TAG, "Package " + packageName + ": " + message);
1236                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, message);
1237                        continue;
1238                    }
1239
1240                    if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
1241                        Log.w(TAG, "Signature mismatch restoring " + packageName);
1242                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
1243                                "Signature mismatch");
1244                        continue;
1245                    }
1246
1247                    if (DEBUG) Log.v(TAG, "Package " + packageName
1248                            + " restore version [" + metaInfo.versionCode
1249                            + "] is compatible with installed version ["
1250                            + packageInfo.versionCode + "]");
1251
1252                    // Now perform the actual restore:  first clear the app's data
1253                    // if appropriate
1254                    clearApplicationDataSynchronous(packageName);
1255
1256                    // Then set up and bind the agent (with a restricted Application object
1257                    // unless the application says otherwise)
1258                    boolean useRealApp = (packageInfo.applicationInfo.flags
1259                            & ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION) != 0;
1260                    if (DEBUG && useRealApp) {
1261                        Log.v(TAG, "agent requires real Application subclass for restore");
1262                    }
1263                    IBackupAgent agent = bindToAgentSynchronous(
1264                            packageInfo.applicationInfo,
1265                            (useRealApp ? IApplicationThread.BACKUP_MODE_INCREMENTAL
1266                                    : IApplicationThread.BACKUP_MODE_RESTORE));
1267                    if (agent == null) {
1268                        Log.w(TAG, "Can't find backup agent for " + packageName);
1269                        EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
1270                                "Restore agent missing");
1271                        continue;
1272                    }
1273
1274                    // And then finally run the restore on this agent
1275                    try {
1276                        processOneRestore(packageInfo, metaInfo.versionCode, agent);
1277                        ++count;
1278                    } finally {
1279                        // unbind and tidy up even on timeout or failure, just in case
1280                        mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
1281
1282                        // The agent was probably running with a stub Application object,
1283                        // which isn't a valid run mode for the main app logic.  Shut
1284                        // down the app so that next time it's launched, it gets the
1285                        // usual full initialization.
1286                        if ((packageInfo.applicationInfo.flags
1287                                & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
1288                            if (DEBUG) Log.d(TAG, "Restore complete, killing host process of "
1289                                    + packageInfo.applicationInfo.processName);
1290                            mActivityManager.killApplicationProcess(
1291                                    packageInfo.applicationInfo.processName,
1292                                    packageInfo.applicationInfo.uid);
1293                        }
1294                    }
1295                }
1296
1297                // if we get this far, report success to the observer
1298                error = 0;
1299                int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
1300                EventLog.writeEvent(RESTORE_SUCCESS_EVENT, count, millis);
1301            } catch (Exception e) {
1302                Log.e(TAG, "Error in restore thread", e);
1303            } finally {
1304                if (DEBUG) Log.d(TAG, "finishing restore mObserver=" + mObserver);
1305
1306                try {
1307                    mTransport.finishRestore();
1308                } catch (RemoteException e) {
1309                    Log.e(TAG, "Error finishing restore", e);
1310                }
1311
1312                if (mObserver != null) {
1313                    try {
1314                        mObserver.restoreFinished(error);
1315                    } catch (RemoteException e) {
1316                        Log.d(TAG, "Restore observer died at restoreFinished");
1317                    }
1318                }
1319
1320                // done; we can finally release the wakelock
1321                mWakelock.release();
1322            }
1323        }
1324
1325        // Do the guts of a restore of one application, using mTransport.getRestoreData().
1326        void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
1327            // !!! TODO: actually run the restore through mTransport
1328            final String packageName = app.packageName;
1329
1330            if (DEBUG) Log.d(TAG, "processOneRestore packageName=" + packageName);
1331
1332            // Don't restore to unprivileged packages
1333            if (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA,
1334                    packageName) != PackageManager.PERMISSION_GRANTED) {
1335                Log.d(TAG, "Skipping restore of unprivileged package " + packageName);
1336            }
1337
1338            // !!! TODO: get the dirs from the transport
1339            File backupDataName = new File(mDataDir, packageName + ".restore");
1340            File newStateName = new File(mStateDir, packageName + ".new");
1341            File savedStateName = new File(mStateDir, packageName);
1342
1343            ParcelFileDescriptor backupData = null;
1344            ParcelFileDescriptor newState = null;
1345
1346            try {
1347                // Run the transport's restore pass
1348                backupData = ParcelFileDescriptor.open(backupDataName,
1349                            ParcelFileDescriptor.MODE_READ_WRITE |
1350                            ParcelFileDescriptor.MODE_CREATE |
1351                            ParcelFileDescriptor.MODE_TRUNCATE);
1352
1353                if (!mTransport.getRestoreData(backupData)) {
1354                    Log.e(TAG, "Error getting restore data for " + packageName);
1355                    EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
1356                    return;
1357                }
1358
1359                // Okay, we have the data.  Now have the agent do the restore.
1360                backupData.close();
1361                backupData = ParcelFileDescriptor.open(backupDataName,
1362                            ParcelFileDescriptor.MODE_READ_ONLY);
1363
1364                newState = ParcelFileDescriptor.open(newStateName,
1365                            ParcelFileDescriptor.MODE_READ_WRITE |
1366                            ParcelFileDescriptor.MODE_CREATE |
1367                            ParcelFileDescriptor.MODE_TRUNCATE);
1368
1369                agent.doRestore(backupData, appVersionCode, newState);
1370
1371                // if everything went okay, remember the recorded state now
1372                newStateName.renameTo(savedStateName);
1373                int size = (int) backupDataName.length();
1374                EventLog.writeEvent(RESTORE_PACKAGE_EVENT, packageName, size);
1375            } catch (Exception e) {
1376                Log.e(TAG, "Error restoring data for " + packageName, e);
1377                EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, e.toString());
1378
1379                // If the agent fails restore, it might have put the app's data
1380                // into an incoherent state.  For consistency we wipe its data
1381                // again in this case before propagating the exception
1382                clearApplicationDataSynchronous(packageName);
1383            } finally {
1384                backupDataName.delete();
1385                try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1386                try { if (newState != null) newState.close(); } catch (IOException e) {}
1387                backupData = newState = null;
1388            }
1389        }
1390    }
1391
1392    class PerformClearThread extends Thread {
1393        IBackupTransport mTransport;
1394        PackageInfo mPackage;
1395
1396        PerformClearThread(IBackupTransport transport, PackageInfo packageInfo) {
1397            mTransport = transport;
1398            mPackage = packageInfo;
1399        }
1400
1401        @Override
1402        public void run() {
1403            try {
1404                // Clear the on-device backup state to ensure a full backup next time
1405                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
1406                File stateFile = new File(stateDir, mPackage.packageName);
1407                stateFile.delete();
1408
1409                // Tell the transport to remove all the persistent storage for the app
1410                mTransport.clearBackupData(mPackage);
1411            } catch (RemoteException e) {
1412                // can't happen; the transport is local
1413            } finally {
1414                try {
1415                    mTransport.finishBackup();
1416                } catch (RemoteException e) {
1417                    // can't happen; the transport is local
1418                }
1419
1420                // Last but not least, release the cpu
1421                mWakelock.release();
1422            }
1423        }
1424    }
1425
1426
1427    // ----- IBackupManager binder interface -----
1428
1429    public void dataChanged(String packageName) throws RemoteException {
1430        // Record that we need a backup pass for the caller.  Since multiple callers
1431        // may share a uid, we need to note all candidates within that uid and schedule
1432        // a backup pass for each of them.
1433        EventLog.writeEvent(BACKUP_DATA_CHANGED_EVENT, packageName);
1434
1435        // If the caller does not hold the BACKUP permission, it can only request a
1436        // backup of its own data.
1437        HashSet<ApplicationInfo> targets;
1438        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
1439                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
1440            targets = mBackupParticipants.get(Binder.getCallingUid());
1441        } else {
1442            // a caller with full permission can ask to back up any participating app
1443            // !!! TODO: allow backup of ANY app?
1444            targets = new HashSet<ApplicationInfo>();
1445            int N = mBackupParticipants.size();
1446            for (int i = 0; i < N; i++) {
1447                HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
1448                if (s != null) {
1449                    targets.addAll(s);
1450                }
1451            }
1452        }
1453        if (targets != null) {
1454            synchronized (mQueueLock) {
1455                // Note that this client has made data changes that need to be backed up
1456                for (ApplicationInfo app : targets) {
1457                    // validate the caller-supplied package name against the known set of
1458                    // packages associated with this uid
1459                    if (app.packageName.equals(packageName)) {
1460                        // Add the caller to the set of pending backups.  If there is
1461                        // one already there, then overwrite it, but no harm done.
1462                        BackupRequest req = new BackupRequest(app, false);
1463                        if (mPendingBackups.put(app, req) == null) {
1464                            // Journal this request in case of crash.  The put()
1465                            // operation returned null when this package was not already
1466                            // in the set; we want to avoid touching the disk redundantly.
1467                            writeToJournalLocked(packageName);
1468
1469                            if (DEBUG) {
1470                                int numKeys = mPendingBackups.size();
1471                                Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
1472                                for (BackupRequest b : mPendingBackups.values()) {
1473                                    Log.d(TAG, "    + " + b + " agent=" + b.appInfo.backupAgentName);
1474                                }
1475                            }
1476                        }
1477                    }
1478                }
1479            }
1480        } else {
1481            Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
1482                    + " uid=" + Binder.getCallingUid());
1483        }
1484    }
1485
1486    private void writeToJournalLocked(String str) {
1487        if (mJournalStream != null) {
1488            try {
1489                mJournalStream.writeUTF(str);
1490            } catch (IOException e) {
1491                Log.e(TAG, "Error writing to backup journal");
1492                mJournalStream = null;
1493                mJournal = null;
1494            }
1495        }
1496    }
1497
1498    // Clear the given package's backup data from the current transport
1499    public void clearBackupData(String packageName) {
1500        if (DEBUG) Log.v(TAG, "clearBackupData() of " + packageName);
1501        PackageInfo info;
1502        try {
1503            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
1504        } catch (NameNotFoundException e) {
1505            Log.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
1506            return;
1507        }
1508
1509        // If the caller does not hold the BACKUP permission, it can only request a
1510        // wipe of its own backed-up data.
1511        HashSet<ApplicationInfo> apps;
1512        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
1513                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
1514            apps = mBackupParticipants.get(Binder.getCallingUid());
1515        } else {
1516            // a caller with full permission can ask to back up any participating app
1517            // !!! TODO: allow data-clear of ANY app?
1518            if (DEBUG) Log.v(TAG, "Privileged caller, allowing clear of other apps");
1519            apps = new HashSet<ApplicationInfo>();
1520            int N = mBackupParticipants.size();
1521            for (int i = 0; i < N; i++) {
1522                HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
1523                if (s != null) {
1524                    apps.addAll(s);
1525                }
1526            }
1527        }
1528
1529        // now find the given package in the set of candidate apps
1530        for (ApplicationInfo app : apps) {
1531            if (app.packageName.equals(packageName)) {
1532                if (DEBUG) Log.v(TAG, "Found the app - running clear process");
1533                // found it; fire off the clear request
1534                synchronized (mQueueLock) {
1535                    long oldId = Binder.clearCallingIdentity();
1536                    mWakelock.acquire();
1537                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
1538                            new ClearParams(getTransport(mCurrentTransport), info));
1539                    mBackupHandler.sendMessage(msg);
1540                    Binder.restoreCallingIdentity(oldId);
1541                }
1542                break;
1543            }
1544        }
1545    }
1546
1547    // Run a backup pass immediately for any applications that have declared
1548    // that they have pending updates.
1549    public void backupNow() throws RemoteException {
1550        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
1551
1552        if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
1553        synchronized (mQueueLock) {
1554            try {
1555                if (DEBUG) Log.v(TAG, "sending immediate backup broadcast");
1556                mRunBackupIntent.send();
1557            } catch (PendingIntent.CanceledException e) {
1558                // should never happen
1559                Log.e(TAG, "run-backup intent cancelled!");
1560            }
1561        }
1562    }
1563
1564    // Enable/disable the backup service
1565    public void setBackupEnabled(boolean enable) {
1566        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1567                "setBackupEnabled");
1568
1569        boolean wasEnabled = mEnabled;
1570        synchronized (this) {
1571            Settings.Secure.putInt(mContext.getContentResolver(),
1572                    Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
1573            mEnabled = enable;
1574        }
1575
1576        synchronized (mQueueLock) {
1577            if (enable && !wasEnabled && mProvisioned) {
1578                // if we've just been enabled, start scheduling backup passes
1579                startBackupAlarmsLocked(BACKUP_INTERVAL);
1580            } else if (!enable) {
1581                // No longer enabled, so stop running backups
1582                mAlarmManager.cancel(mRunBackupIntent);
1583            }
1584        }
1585    }
1586
1587    // Mark the backup service as having been provisioned
1588    public void setBackupProvisioned(boolean available) {
1589        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1590                "setBackupProvisioned");
1591
1592        boolean wasProvisioned = mProvisioned;
1593        synchronized (this) {
1594            Settings.Secure.putInt(mContext.getContentResolver(),
1595                    Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
1596            mProvisioned = available;
1597        }
1598
1599        synchronized (mQueueLock) {
1600            if (available && !wasProvisioned && mEnabled) {
1601                // we're now good to go, so start the backup alarms
1602                startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
1603            } else if (!available) {
1604                // No longer enabled, so stop running backups
1605                Log.w(TAG, "Backup service no longer provisioned");
1606                mAlarmManager.cancel(mRunBackupIntent);
1607            }
1608        }
1609    }
1610
1611    private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
1612        long when = System.currentTimeMillis() + delayBeforeFirstBackup;
1613        mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, when,
1614                BACKUP_INTERVAL, mRunBackupIntent);
1615    }
1616
1617    // Report whether the backup mechanism is currently enabled
1618    public boolean isBackupEnabled() {
1619        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
1620        return mEnabled;    // no need to synchronize just to read it
1621    }
1622
1623    // Report the name of the currently active transport
1624    public String getCurrentTransport() {
1625        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1626                "getCurrentTransport");
1627        Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
1628        return mCurrentTransport;
1629    }
1630
1631    // Report all known, available backup transports
1632    public String[] listAllTransports() {
1633        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
1634
1635        String[] list = null;
1636        ArrayList<String> known = new ArrayList<String>();
1637        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
1638            if (entry.getValue() != null) {
1639                known.add(entry.getKey());
1640            }
1641        }
1642
1643        if (known.size() > 0) {
1644            list = new String[known.size()];
1645            known.toArray(list);
1646        }
1647        return list;
1648    }
1649
1650    // Select which transport to use for the next backup operation.  If the given
1651    // name is not one of the available transports, no action is taken and the method
1652    // returns null.
1653    public String selectBackupTransport(String transport) {
1654        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
1655
1656        synchronized (mTransports) {
1657            String prevTransport = null;
1658            if (mTransports.get(transport) != null) {
1659                prevTransport = mCurrentTransport;
1660                mCurrentTransport = transport;
1661                Settings.Secure.putString(mContext.getContentResolver(),
1662                        Settings.Secure.BACKUP_TRANSPORT, transport);
1663                Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport
1664                        + " returning " + prevTransport);
1665            } else {
1666                Log.w(TAG, "Attempt to select unavailable transport " + transport);
1667            }
1668            return prevTransport;
1669        }
1670    }
1671
1672    // Callback: a requested backup agent has been instantiated.  This should only
1673    // be called from the Activity Manager.
1674    public void agentConnected(String packageName, IBinder agentBinder) {
1675        synchronized(mAgentConnectLock) {
1676            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
1677                Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
1678                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
1679                mConnectedAgent = agent;
1680                mConnecting = false;
1681            } else {
1682                Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
1683                        + " claiming agent connected");
1684            }
1685            mAgentConnectLock.notifyAll();
1686        }
1687    }
1688
1689    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
1690    // If the agent failed to come up in the first place, the agentBinder argument
1691    // will be null.  This should only be called from the Activity Manager.
1692    public void agentDisconnected(String packageName) {
1693        // TODO: handle backup being interrupted
1694        synchronized(mAgentConnectLock) {
1695            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
1696                mConnectedAgent = null;
1697                mConnecting = false;
1698            } else {
1699                Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
1700                        + " claiming agent disconnected");
1701            }
1702            mAgentConnectLock.notifyAll();
1703        }
1704    }
1705
1706    // Hand off a restore session
1707    public IRestoreSession beginRestoreSession(String transport) {
1708        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
1709
1710        synchronized(this) {
1711            if (mActiveRestoreSession != null) {
1712                Log.d(TAG, "Restore session requested but one already active");
1713                return null;
1714            }
1715            mActiveRestoreSession = new RestoreSession(transport);
1716        }
1717        return mActiveRestoreSession;
1718    }
1719
1720    // ----- Restore session -----
1721
1722    class RestoreSession extends IRestoreSession.Stub {
1723        private static final String TAG = "RestoreSession";
1724
1725        private IBackupTransport mRestoreTransport = null;
1726        RestoreSet[] mRestoreSets = null;
1727
1728        RestoreSession(String transport) {
1729            mRestoreTransport = getTransport(transport);
1730        }
1731
1732        // --- Binder interface ---
1733        public synchronized RestoreSet[] getAvailableRestoreSets() {
1734            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1735                    "getAvailableRestoreSets");
1736
1737            try {
1738                if (mRestoreTransport == null) {
1739                    Log.w(TAG, "Null transport getting restore sets");
1740                    return null;
1741                }
1742                if (mRestoreSets == null) { // valid transport; do the one-time fetch
1743                    mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
1744                    if (mRestoreSets == null) EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
1745                }
1746                return mRestoreSets;
1747            } catch (Exception e) {
1748                Log.e(TAG, "Error in getAvailableRestoreSets", e);
1749                return null;
1750            }
1751        }
1752
1753        public synchronized int performRestore(long token, IRestoreObserver observer) {
1754            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1755                    "performRestore");
1756
1757            if (DEBUG) Log.d(TAG, "performRestore token=" + Long.toHexString(token)
1758                    + " observer=" + observer);
1759
1760            if (mRestoreTransport == null || mRestoreSets == null) {
1761                Log.e(TAG, "Ignoring performRestore() with no restore set");
1762                return -1;
1763            }
1764
1765            for (int i = 0; i < mRestoreSets.length; i++) {
1766                if (token == mRestoreSets[i].token) {
1767                    long oldId = Binder.clearCallingIdentity();
1768                    mWakelock.acquire();
1769                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
1770                    msg.obj = new RestoreParams(mRestoreTransport, observer, token);
1771                    mBackupHandler.sendMessage(msg);
1772                    Binder.restoreCallingIdentity(oldId);
1773                    return 0;
1774                }
1775            }
1776
1777            Log.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
1778            return -1;
1779        }
1780
1781        public synchronized void endRestoreSession() {
1782            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1783                    "endRestoreSession");
1784
1785            if (DEBUG) Log.d(TAG, "endRestoreSession");
1786
1787            synchronized (this) {
1788                try {
1789                    if (mRestoreTransport != null) mRestoreTransport.finishRestore();
1790                } catch (Exception e) {
1791                    Log.e(TAG, "Error in finishRestore", e);
1792                } finally {
1793                    mRestoreTransport = null;
1794                }
1795            }
1796
1797            synchronized (BackupManagerService.this) {
1798                if (BackupManagerService.this.mActiveRestoreSession == this) {
1799                    BackupManagerService.this.mActiveRestoreSession = null;
1800                } else {
1801                    Log.e(TAG, "ending non-current restore session");
1802                }
1803            }
1804        }
1805    }
1806
1807
1808    @Override
1809    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1810        synchronized (mQueueLock) {
1811            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
1812                    + " / " + (!mProvisioned ? "not " : "") + "provisioned");
1813            pw.println("Available transports:");
1814            for (String t : listAllTransports()) {
1815                String pad = (t.equals(mCurrentTransport)) ? "  * " : "    ";
1816                pw.println(pad + t);
1817            }
1818            int N = mBackupParticipants.size();
1819            pw.println("Participants: " + N);
1820            for (int i=0; i<N; i++) {
1821                int uid = mBackupParticipants.keyAt(i);
1822                pw.print("  uid: ");
1823                pw.println(uid);
1824                HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
1825                for (ApplicationInfo app: participants) {
1826                    pw.println("    " + app.toString());
1827                }
1828            }
1829            pw.println("Ever backed up: " + mEverStoredApps.size());
1830            for (String pkg : mEverStoredApps) {
1831                pw.println("    " + pkg);
1832            }
1833            pw.println("Pending: " + mPendingBackups.size());
1834            for (BackupRequest req : mPendingBackups.values()) {
1835                pw.println("    " + req);
1836            }
1837        }
1838    }
1839}
1840