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