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