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