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 17 package com.android.server; 18 19 import android.app.ActivityManagerNative; 20 import android.app.AlarmManager; 21 import android.app.AppGlobals; 22 import android.app.IActivityManager; 23 import android.app.IApplicationThread; 24 import android.app.IBackupAgent; 25 import android.app.PendingIntent; 26 import android.app.backup.BackupAgent; 27 import android.app.backup.BackupDataOutput; 28 import android.app.backup.FullBackup; 29 import android.app.backup.RestoreSet; 30 import android.app.backup.IBackupManager; 31 import android.app.backup.IFullBackupRestoreObserver; 32 import android.app.backup.IRestoreObserver; 33 import android.app.backup.IRestoreSession; 34 import android.content.ActivityNotFoundException; 35 import android.content.BroadcastReceiver; 36 import android.content.ComponentName; 37 import android.content.ContentResolver; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.IntentFilter; 41 import android.content.ServiceConnection; 42 import android.content.pm.ApplicationInfo; 43 import android.content.pm.IPackageDataObserver; 44 import android.content.pm.IPackageDeleteObserver; 45 import android.content.pm.IPackageInstallObserver; 46 import android.content.pm.IPackageManager; 47 import android.content.pm.PackageInfo; 48 import android.content.pm.PackageManager; 49 import android.content.pm.Signature; 50 import android.content.pm.PackageManager.NameNotFoundException; 51 import android.database.ContentObserver; 52 import android.net.Uri; 53 import android.os.Binder; 54 import android.os.Build; 55 import android.os.Bundle; 56 import android.os.Environment; 57 import android.os.Handler; 58 import android.os.HandlerThread; 59 import android.os.IBinder; 60 import android.os.Looper; 61 import android.os.Message; 62 import android.os.ParcelFileDescriptor; 63 import android.os.PowerManager; 64 import android.os.Process; 65 import android.os.RemoteException; 66 import android.os.SELinux; 67 import android.os.ServiceManager; 68 import android.os.SystemClock; 69 import android.os.UserHandle; 70 import android.os.WorkSource; 71 import android.os.Environment.UserEnvironment; 72 import android.os.storage.IMountService; 73 import android.provider.Settings; 74 import android.util.EventLog; 75 import android.util.Log; 76 import android.util.Slog; 77 import android.util.SparseArray; 78 import android.util.StringBuilderPrinter; 79 80 import com.android.internal.backup.BackupConstants; 81 import com.android.internal.backup.IBackupTransport; 82 import com.android.internal.backup.IObbBackupService; 83 import com.android.internal.backup.LocalTransport; 84 import com.android.server.PackageManagerBackupAgent.Metadata; 85 86 import java.io.BufferedInputStream; 87 import java.io.BufferedOutputStream; 88 import java.io.ByteArrayOutputStream; 89 import java.io.DataInputStream; 90 import java.io.DataOutputStream; 91 import java.io.EOFException; 92 import java.io.File; 93 import java.io.FileDescriptor; 94 import java.io.FileInputStream; 95 import java.io.FileNotFoundException; 96 import java.io.FileOutputStream; 97 import java.io.IOException; 98 import java.io.InputStream; 99 import java.io.OutputStream; 100 import java.io.PrintWriter; 101 import java.io.RandomAccessFile; 102 import java.security.InvalidAlgorithmParameterException; 103 import java.security.InvalidKeyException; 104 import java.security.Key; 105 import java.security.NoSuchAlgorithmException; 106 import java.security.SecureRandom; 107 import java.security.spec.InvalidKeySpecException; 108 import java.security.spec.KeySpec; 109 import java.text.SimpleDateFormat; 110 import java.util.ArrayList; 111 import java.util.Arrays; 112 import java.util.Date; 113 import java.util.HashMap; 114 import java.util.HashSet; 115 import java.util.List; 116 import java.util.Map; 117 import java.util.Random; 118 import java.util.Set; 119 import java.util.concurrent.atomic.AtomicBoolean; 120 import java.util.zip.Deflater; 121 import java.util.zip.DeflaterOutputStream; 122 import java.util.zip.InflaterInputStream; 123 124 import javax.crypto.BadPaddingException; 125 import javax.crypto.Cipher; 126 import javax.crypto.CipherInputStream; 127 import javax.crypto.CipherOutputStream; 128 import javax.crypto.IllegalBlockSizeException; 129 import javax.crypto.NoSuchPaddingException; 130 import javax.crypto.SecretKey; 131 import javax.crypto.SecretKeyFactory; 132 import javax.crypto.spec.IvParameterSpec; 133 import javax.crypto.spec.PBEKeySpec; 134 import javax.crypto.spec.SecretKeySpec; 135 136 class BackupManagerService extends IBackupManager.Stub { 137 private static final String TAG = "BackupManagerService"; 138 private static final boolean DEBUG = true; 139 private static final boolean MORE_DEBUG = false; 140 141 // Name and current contents version of the full-backup manifest file 142 static final String BACKUP_MANIFEST_FILENAME = "_manifest"; 143 static final int BACKUP_MANIFEST_VERSION = 1; 144 static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n"; 145 static final int BACKUP_FILE_VERSION = 1; 146 static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production 147 148 static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup"; 149 150 // How often we perform a backup pass. Privileged external callers can 151 // trigger an immediate pass. 152 private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR; 153 154 // Random variation in backup scheduling time to avoid server load spikes 155 private static final int FUZZ_MILLIS = 5 * 60 * 1000; 156 157 // The amount of time between the initial provisioning of the device and 158 // the first backup pass. 159 private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR; 160 161 private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN"; 162 private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT"; 163 private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR"; 164 private static final int MSG_RUN_BACKUP = 1; 165 private static final int MSG_RUN_FULL_BACKUP = 2; 166 private static final int MSG_RUN_RESTORE = 3; 167 private static final int MSG_RUN_CLEAR = 4; 168 private static final int MSG_RUN_INITIALIZE = 5; 169 private static final int MSG_RUN_GET_RESTORE_SETS = 6; 170 private static final int MSG_TIMEOUT = 7; 171 private static final int MSG_RESTORE_TIMEOUT = 8; 172 private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9; 173 private static final int MSG_RUN_FULL_RESTORE = 10; 174 175 // backup task state machine tick 176 static final int MSG_BACKUP_RESTORE_STEP = 20; 177 static final int MSG_OP_COMPLETE = 21; 178 179 // Timeout interval for deciding that a bind or clear-data has taken too long 180 static final long TIMEOUT_INTERVAL = 10 * 1000; 181 182 // Timeout intervals for agent backup & restore operations 183 static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000; 184 static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000; 185 static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000; 186 static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000; 187 188 // User confirmation timeout for a full backup/restore operation. It's this long in 189 // order to give them time to enter the backup password. 190 static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000; 191 192 private Context mContext; 193 private PackageManager mPackageManager; 194 IPackageManager mPackageManagerBinder; 195 private IActivityManager mActivityManager; 196 private PowerManager mPowerManager; 197 private AlarmManager mAlarmManager; 198 private IMountService mMountService; 199 IBackupManager mBackupManagerBinder; 200 201 boolean mEnabled; // access to this is synchronized on 'this' 202 boolean mProvisioned; 203 boolean mAutoRestore; 204 PowerManager.WakeLock mWakelock; 205 HandlerThread mHandlerThread; 206 BackupHandler mBackupHandler; 207 PendingIntent mRunBackupIntent, mRunInitIntent; 208 BroadcastReceiver mRunBackupReceiver, mRunInitReceiver; 209 // map UIDs to the set of participating packages under that UID 210 final SparseArray<HashSet<String>> mBackupParticipants 211 = new SparseArray<HashSet<String>>(); 212 // set of backup services that have pending changes 213 class BackupRequest { 214 public String packageName; 215 216 BackupRequest(String pkgName) { 217 packageName = pkgName; 218 } 219 220 public String toString() { 221 return "BackupRequest{pkg=" + packageName + "}"; 222 } 223 } 224 // Backups that we haven't started yet. Keys are package names. 225 HashMap<String,BackupRequest> mPendingBackups 226 = new HashMap<String,BackupRequest>(); 227 228 // Pseudoname that we use for the Package Manager metadata "package" 229 static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; 230 231 // locking around the pending-backup management 232 final Object mQueueLock = new Object(); 233 234 // The thread performing the sequence of queued backups binds to each app's agent 235 // in succession. Bind notifications are asynchronously delivered through the 236 // Activity Manager; use this lock object to signal when a requested binding has 237 // completed. 238 final Object mAgentConnectLock = new Object(); 239 IBackupAgent mConnectedAgent; 240 volatile boolean mBackupRunning; 241 volatile boolean mConnecting; 242 volatile long mLastBackupPass; 243 volatile long mNextBackupPass; 244 245 // For debugging, we maintain a progress trace of operations during backup 246 static final boolean DEBUG_BACKUP_TRACE = true; 247 final List<String> mBackupTrace = new ArrayList<String>(); 248 249 // A similar synchronization mechanism around clearing apps' data for restore 250 final Object mClearDataLock = new Object(); 251 volatile boolean mClearingData; 252 253 // Transport bookkeeping 254 final HashMap<String,IBackupTransport> mTransports 255 = new HashMap<String,IBackupTransport>(); 256 String mCurrentTransport; 257 IBackupTransport mLocalTransport, mGoogleTransport; 258 ActiveRestoreSession mActiveRestoreSession; 259 260 // Watch the device provisioning operation during setup 261 ContentObserver mProvisionedObserver; 262 263 class ProvisionedObserver extends ContentObserver { 264 public ProvisionedObserver(Handler handler) { 265 super(handler); 266 } 267 268 public void onChange(boolean selfChange) { 269 final boolean wasProvisioned = mProvisioned; 270 final boolean isProvisioned = deviceIsProvisioned(); 271 // latch: never unprovision 272 mProvisioned = wasProvisioned || isProvisioned; 273 if (MORE_DEBUG) { 274 Slog.d(TAG, "Provisioning change: was=" + wasProvisioned 275 + " is=" + isProvisioned + " now=" + mProvisioned); 276 } 277 278 synchronized (mQueueLock) { 279 if (mProvisioned && !wasProvisioned && mEnabled) { 280 // we're now good to go, so start the backup alarms 281 if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups"); 282 startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL); 283 } 284 } 285 } 286 } 287 288 class RestoreGetSetsParams { 289 public IBackupTransport transport; 290 public ActiveRestoreSession session; 291 public IRestoreObserver observer; 292 293 RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session, 294 IRestoreObserver _observer) { 295 transport = _transport; 296 session = _session; 297 observer = _observer; 298 } 299 } 300 301 class RestoreParams { 302 public IBackupTransport transport; 303 public IRestoreObserver observer; 304 public long token; 305 public PackageInfo pkgInfo; 306 public int pmToken; // in post-install restore, the PM's token for this transaction 307 public boolean needFullBackup; 308 public String[] filterSet; 309 310 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, 311 long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) { 312 transport = _transport; 313 observer = _obs; 314 token = _token; 315 pkgInfo = _pkg; 316 pmToken = _pmToken; 317 needFullBackup = _needFullBackup; 318 filterSet = null; 319 } 320 321 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, 322 boolean _needFullBackup) { 323 transport = _transport; 324 observer = _obs; 325 token = _token; 326 pkgInfo = null; 327 pmToken = 0; 328 needFullBackup = _needFullBackup; 329 filterSet = null; 330 } 331 332 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token, 333 String[] _filterSet, boolean _needFullBackup) { 334 transport = _transport; 335 observer = _obs; 336 token = _token; 337 pkgInfo = null; 338 pmToken = 0; 339 needFullBackup = _needFullBackup; 340 filterSet = _filterSet; 341 } 342 } 343 344 class ClearParams { 345 public IBackupTransport transport; 346 public PackageInfo packageInfo; 347 348 ClearParams(IBackupTransport _transport, PackageInfo _info) { 349 transport = _transport; 350 packageInfo = _info; 351 } 352 } 353 354 class FullParams { 355 public ParcelFileDescriptor fd; 356 public final AtomicBoolean latch; 357 public IFullBackupRestoreObserver observer; 358 public String curPassword; // filled in by the confirmation step 359 public String encryptPassword; 360 361 FullParams() { 362 latch = new AtomicBoolean(false); 363 } 364 } 365 366 class FullBackupParams extends FullParams { 367 public boolean includeApks; 368 public boolean includeObbs; 369 public boolean includeShared; 370 public boolean allApps; 371 public boolean includeSystem; 372 public String[] packages; 373 374 FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs, 375 boolean saveShared, boolean doAllApps, boolean doSystem, String[] pkgList) { 376 fd = output; 377 includeApks = saveApks; 378 includeObbs = saveObbs; 379 includeShared = saveShared; 380 allApps = doAllApps; 381 includeSystem = doSystem; 382 packages = pkgList; 383 } 384 } 385 386 class FullRestoreParams extends FullParams { 387 FullRestoreParams(ParcelFileDescriptor input) { 388 fd = input; 389 } 390 } 391 392 // Bookkeeping of in-flight operations for timeout etc. purposes. The operation 393 // token is the index of the entry in the pending-operations list. 394 static final int OP_PENDING = 0; 395 static final int OP_ACKNOWLEDGED = 1; 396 static final int OP_TIMEOUT = -1; 397 398 class Operation { 399 public int state; 400 public BackupRestoreTask callback; 401 402 Operation(int initialState, BackupRestoreTask callbackObj) { 403 state = initialState; 404 callback = callbackObj; 405 } 406 } 407 final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>(); 408 final Object mCurrentOpLock = new Object(); 409 final Random mTokenGenerator = new Random(); 410 411 final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>(); 412 413 // Where we keep our journal files and other bookkeeping 414 File mBaseStateDir; 415 File mDataDir; 416 File mJournalDir; 417 File mJournal; 418 419 // Backup password, if any, and the file where it's saved. What is stored is not the 420 // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but 421 // persisted) salt. Validation is performed by running the challenge text through the 422 // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches 423 // the saved hash string, then the challenge text matches the originally supplied 424 // password text. 425 private final SecureRandom mRng = new SecureRandom(); 426 private String mPasswordHash; 427 private File mPasswordHashFile; 428 private byte[] mPasswordSalt; 429 430 // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys 431 static final int PBKDF2_HASH_ROUNDS = 10000; 432 static final int PBKDF2_KEY_SIZE = 256; // bits 433 static final int PBKDF2_SALT_SIZE = 512; // bits 434 static final String ENCRYPTION_ALGORITHM_NAME = "AES-256"; 435 436 // Keep a log of all the apps we've ever backed up, and what the 437 // dataset tokens are for both the current backup dataset and 438 // the ancestral dataset. 439 private File mEverStored; 440 HashSet<String> mEverStoredApps = new HashSet<String>(); 441 442 static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes 443 File mTokenFile; 444 Set<String> mAncestralPackages = null; 445 long mAncestralToken = 0; 446 long mCurrentToken = 0; 447 448 // Persistently track the need to do a full init 449 static final String INIT_SENTINEL_FILE_NAME = "_need_init_"; 450 HashSet<String> mPendingInits = new HashSet<String>(); // transport names 451 452 // Utility: build a new random integer token 453 int generateToken() { 454 int token; 455 do { 456 synchronized (mTokenGenerator) { 457 token = mTokenGenerator.nextInt(); 458 } 459 } while (token < 0); 460 return token; 461 } 462 463 // ----- Asynchronous backup/restore handler thread ----- 464 465 private class BackupHandler extends Handler { 466 public BackupHandler(Looper looper) { 467 super(looper); 468 } 469 470 public void handleMessage(Message msg) { 471 472 switch (msg.what) { 473 case MSG_RUN_BACKUP: 474 { 475 mLastBackupPass = System.currentTimeMillis(); 476 mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL; 477 478 IBackupTransport transport = getTransport(mCurrentTransport); 479 if (transport == null) { 480 Slog.v(TAG, "Backup requested but no transport available"); 481 synchronized (mQueueLock) { 482 mBackupRunning = false; 483 } 484 mWakelock.release(); 485 break; 486 } 487 488 // snapshot the pending-backup set and work on that 489 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>(); 490 File oldJournal = mJournal; 491 synchronized (mQueueLock) { 492 // Do we have any work to do? Construct the work queue 493 // then release the synchronization lock to actually run 494 // the backup. 495 if (mPendingBackups.size() > 0) { 496 for (BackupRequest b: mPendingBackups.values()) { 497 queue.add(b); 498 } 499 if (DEBUG) Slog.v(TAG, "clearing pending backups"); 500 mPendingBackups.clear(); 501 502 // Start a new backup-queue journal file too 503 mJournal = null; 504 505 } 506 } 507 508 // At this point, we have started a new journal file, and the old 509 // file identity is being passed to the backup processing task. 510 // When it completes successfully, that old journal file will be 511 // deleted. If we crash prior to that, the old journal is parsed 512 // at next boot and the journaled requests fulfilled. 513 if (queue.size() > 0) { 514 // Spin up a backup state sequence and set it running 515 PerformBackupTask pbt = new PerformBackupTask(transport, queue, oldJournal); 516 Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt); 517 sendMessage(pbtMessage); 518 } else { 519 Slog.v(TAG, "Backup requested but nothing pending"); 520 synchronized (mQueueLock) { 521 mBackupRunning = false; 522 } 523 mWakelock.release(); 524 } 525 break; 526 } 527 528 case MSG_BACKUP_RESTORE_STEP: 529 { 530 try { 531 BackupRestoreTask task = (BackupRestoreTask) msg.obj; 532 if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing"); 533 task.execute(); 534 } catch (ClassCastException e) { 535 Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj); 536 } 537 break; 538 } 539 540 case MSG_OP_COMPLETE: 541 { 542 try { 543 BackupRestoreTask task = (BackupRestoreTask) msg.obj; 544 task.operationComplete(); 545 } catch (ClassCastException e) { 546 Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj); 547 } 548 break; 549 } 550 551 case MSG_RUN_FULL_BACKUP: 552 { 553 // TODO: refactor full backup to be a looper-based state machine 554 // similar to normal backup/restore. 555 FullBackupParams params = (FullBackupParams)msg.obj; 556 PerformFullBackupTask task = new PerformFullBackupTask(params.fd, 557 params.observer, params.includeApks, params.includeObbs, 558 params.includeShared, params.curPassword, params.encryptPassword, 559 params.allApps, params.includeSystem, params.packages, params.latch); 560 (new Thread(task)).start(); 561 break; 562 } 563 564 case MSG_RUN_RESTORE: 565 { 566 RestoreParams params = (RestoreParams)msg.obj; 567 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); 568 PerformRestoreTask task = new PerformRestoreTask( 569 params.transport, params.observer, 570 params.token, params.pkgInfo, params.pmToken, 571 params.needFullBackup, params.filterSet); 572 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task); 573 sendMessage(restoreMsg); 574 break; 575 } 576 577 case MSG_RUN_FULL_RESTORE: 578 { 579 // TODO: refactor full restore to be a looper-based state machine 580 // similar to normal backup/restore. 581 FullRestoreParams params = (FullRestoreParams)msg.obj; 582 PerformFullRestoreTask task = new PerformFullRestoreTask(params.fd, 583 params.curPassword, params.encryptPassword, 584 params.observer, params.latch); 585 (new Thread(task)).start(); 586 break; 587 } 588 589 case MSG_RUN_CLEAR: 590 { 591 ClearParams params = (ClearParams)msg.obj; 592 (new PerformClearTask(params.transport, params.packageInfo)).run(); 593 break; 594 } 595 596 case MSG_RUN_INITIALIZE: 597 { 598 HashSet<String> queue; 599 600 // Snapshot the pending-init queue and work on that 601 synchronized (mQueueLock) { 602 queue = new HashSet<String>(mPendingInits); 603 mPendingInits.clear(); 604 } 605 606 (new PerformInitializeTask(queue)).run(); 607 break; 608 } 609 610 case MSG_RUN_GET_RESTORE_SETS: 611 { 612 // Like other async operations, this is entered with the wakelock held 613 RestoreSet[] sets = null; 614 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj; 615 try { 616 sets = params.transport.getAvailableRestoreSets(); 617 // cache the result in the active session 618 synchronized (params.session) { 619 params.session.mRestoreSets = sets; 620 } 621 if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 622 } catch (Exception e) { 623 Slog.e(TAG, "Error from transport getting set list"); 624 } finally { 625 if (params.observer != null) { 626 try { 627 params.observer.restoreSetsAvailable(sets); 628 } catch (RemoteException re) { 629 Slog.e(TAG, "Unable to report listing to observer"); 630 } catch (Exception e) { 631 Slog.e(TAG, "Restore observer threw", e); 632 } 633 } 634 635 // Done: reset the session timeout clock 636 removeMessages(MSG_RESTORE_TIMEOUT); 637 sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 638 639 mWakelock.release(); 640 } 641 break; 642 } 643 644 case MSG_TIMEOUT: 645 { 646 handleTimeout(msg.arg1, msg.obj); 647 break; 648 } 649 650 case MSG_RESTORE_TIMEOUT: 651 { 652 synchronized (BackupManagerService.this) { 653 if (mActiveRestoreSession != null) { 654 // Client app left the restore session dangling. We know that it 655 // can't be in the middle of an actual restore operation because 656 // the timeout is suspended while a restore is in progress. Clean 657 // up now. 658 Slog.w(TAG, "Restore session timed out; aborting"); 659 post(mActiveRestoreSession.new EndRestoreRunnable( 660 BackupManagerService.this, mActiveRestoreSession)); 661 } 662 } 663 } 664 665 case MSG_FULL_CONFIRMATION_TIMEOUT: 666 { 667 synchronized (mFullConfirmations) { 668 FullParams params = mFullConfirmations.get(msg.arg1); 669 if (params != null) { 670 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation"); 671 672 // Release the waiter; timeout == completion 673 signalFullBackupRestoreCompletion(params); 674 675 // Remove the token from the set 676 mFullConfirmations.delete(msg.arg1); 677 678 // Report a timeout to the observer, if any 679 if (params.observer != null) { 680 try { 681 params.observer.onTimeout(); 682 } catch (RemoteException e) { 683 /* don't care if the app has gone away */ 684 } 685 } 686 } else { 687 Slog.d(TAG, "couldn't find params for token " + msg.arg1); 688 } 689 } 690 break; 691 } 692 } 693 } 694 } 695 696 // ----- Debug-only backup operation trace ----- 697 void addBackupTrace(String s) { 698 if (DEBUG_BACKUP_TRACE) { 699 synchronized (mBackupTrace) { 700 mBackupTrace.add(s); 701 } 702 } 703 } 704 705 void clearBackupTrace() { 706 if (DEBUG_BACKUP_TRACE) { 707 synchronized (mBackupTrace) { 708 mBackupTrace.clear(); 709 } 710 } 711 } 712 713 // ----- Main service implementation ----- 714 715 public BackupManagerService(Context context) { 716 mContext = context; 717 mPackageManager = context.getPackageManager(); 718 mPackageManagerBinder = AppGlobals.getPackageManager(); 719 mActivityManager = ActivityManagerNative.getDefault(); 720 721 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 722 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 723 mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); 724 725 mBackupManagerBinder = asInterface(asBinder()); 726 727 // spin up the backup/restore handler thread 728 mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND); 729 mHandlerThread.start(); 730 mBackupHandler = new BackupHandler(mHandlerThread.getLooper()); 731 732 // Set up our bookkeeping 733 final ContentResolver resolver = context.getContentResolver(); 734 boolean areEnabled = Settings.Secure.getInt(resolver, 735 Settings.Secure.BACKUP_ENABLED, 0) != 0; 736 mProvisioned = Settings.Global.getInt(resolver, 737 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 738 mAutoRestore = Settings.Secure.getInt(resolver, 739 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0; 740 741 mProvisionedObserver = new ProvisionedObserver(mBackupHandler); 742 resolver.registerContentObserver( 743 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), 744 false, mProvisionedObserver); 745 746 // If Encrypted file systems is enabled or disabled, this call will return the 747 // correct directory. 748 mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup"); 749 mBaseStateDir.mkdirs(); 750 if (!SELinux.restorecon(mBaseStateDir)) { 751 Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir); 752 } 753 mDataDir = Environment.getDownloadCacheDirectory(); 754 755 mPasswordHashFile = new File(mBaseStateDir, "pwhash"); 756 if (mPasswordHashFile.exists()) { 757 FileInputStream fin = null; 758 DataInputStream in = null; 759 try { 760 fin = new FileInputStream(mPasswordHashFile); 761 in = new DataInputStream(new BufferedInputStream(fin)); 762 // integer length of the salt array, followed by the salt, 763 // then the hex pw hash string 764 int saltLen = in.readInt(); 765 byte[] salt = new byte[saltLen]; 766 in.readFully(salt); 767 mPasswordHash = in.readUTF(); 768 mPasswordSalt = salt; 769 } catch (IOException e) { 770 Slog.e(TAG, "Unable to read saved backup pw hash"); 771 } finally { 772 try { 773 if (in != null) in.close(); 774 if (fin != null) fin.close(); 775 } catch (IOException e) { 776 Slog.w(TAG, "Unable to close streams"); 777 } 778 } 779 } 780 781 // Alarm receivers for scheduled backups & initialization operations 782 mRunBackupReceiver = new RunBackupReceiver(); 783 IntentFilter filter = new IntentFilter(); 784 filter.addAction(RUN_BACKUP_ACTION); 785 context.registerReceiver(mRunBackupReceiver, filter, 786 android.Manifest.permission.BACKUP, null); 787 788 mRunInitReceiver = new RunInitializeReceiver(); 789 filter = new IntentFilter(); 790 filter.addAction(RUN_INITIALIZE_ACTION); 791 context.registerReceiver(mRunInitReceiver, filter, 792 android.Manifest.permission.BACKUP, null); 793 794 Intent backupIntent = new Intent(RUN_BACKUP_ACTION); 795 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 796 mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0); 797 798 Intent initIntent = new Intent(RUN_INITIALIZE_ACTION); 799 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 800 mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0); 801 802 // Set up the backup-request journaling 803 mJournalDir = new File(mBaseStateDir, "pending"); 804 mJournalDir.mkdirs(); // creates mBaseStateDir along the way 805 mJournal = null; // will be created on first use 806 807 // Set up the various sorts of package tracking we do 808 initPackageTracking(); 809 810 // Build our mapping of uid to backup client services. This implicitly 811 // schedules a backup pass on the Package Manager metadata the first 812 // time anything needs to be backed up. 813 synchronized (mBackupParticipants) { 814 addPackageParticipantsLocked(null); 815 } 816 817 // Set up our transport options and initialize the default transport 818 // TODO: Have transports register themselves somehow? 819 // TODO: Don't create transports that we don't need to? 820 mLocalTransport = new LocalTransport(context); // This is actually pretty cheap 821 ComponentName localName = new ComponentName(context, LocalTransport.class); 822 registerTransport(localName.flattenToShortString(), mLocalTransport); 823 824 mGoogleTransport = null; 825 mCurrentTransport = Settings.Secure.getString(context.getContentResolver(), 826 Settings.Secure.BACKUP_TRANSPORT); 827 if ("".equals(mCurrentTransport)) { 828 mCurrentTransport = null; 829 } 830 if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport); 831 832 // Attach to the Google backup transport. When this comes up, it will set 833 // itself as the current transport because we explicitly reset mCurrentTransport 834 // to null. 835 ComponentName transportComponent = new ComponentName("com.google.android.backup", 836 "com.google.android.backup.BackupTransportService"); 837 try { 838 // If there's something out there that is supposed to be the Google 839 // backup transport, make sure it's legitimately part of the OS build 840 // and not an app lying about its package name. 841 ApplicationInfo info = mPackageManager.getApplicationInfo( 842 transportComponent.getPackageName(), 0); 843 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 844 if (DEBUG) Slog.v(TAG, "Binding to Google transport"); 845 Intent intent = new Intent().setComponent(transportComponent); 846 context.bindServiceAsUser(intent, mGoogleConnection, Context.BIND_AUTO_CREATE, 847 UserHandle.OWNER); 848 } else { 849 Slog.w(TAG, "Possible Google transport spoof: ignoring " + info); 850 } 851 } catch (PackageManager.NameNotFoundException nnf) { 852 // No such package? No binding. 853 if (DEBUG) Slog.v(TAG, "Google transport not present"); 854 } 855 856 // Now that we know about valid backup participants, parse any 857 // leftover journal files into the pending backup set 858 parseLeftoverJournals(); 859 860 // Power management 861 mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"); 862 863 // Start the backup passes going 864 setBackupEnabled(areEnabled); 865 } 866 867 private class RunBackupReceiver extends BroadcastReceiver { 868 public void onReceive(Context context, Intent intent) { 869 if (RUN_BACKUP_ACTION.equals(intent.getAction())) { 870 synchronized (mQueueLock) { 871 if (mPendingInits.size() > 0) { 872 // If there are pending init operations, we process those 873 // and then settle into the usual periodic backup schedule. 874 if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup"); 875 try { 876 mAlarmManager.cancel(mRunInitIntent); 877 mRunInitIntent.send(); 878 } catch (PendingIntent.CanceledException ce) { 879 Slog.e(TAG, "Run init intent cancelled"); 880 // can't really do more than bail here 881 } 882 } else { 883 // Don't run backups now if we're disabled or not yet 884 // fully set up. 885 if (mEnabled && mProvisioned) { 886 if (!mBackupRunning) { 887 if (DEBUG) Slog.v(TAG, "Running a backup pass"); 888 889 // Acquire the wakelock and pass it to the backup thread. it will 890 // be released once backup concludes. 891 mBackupRunning = true; 892 mWakelock.acquire(); 893 894 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP); 895 mBackupHandler.sendMessage(msg); 896 } else { 897 Slog.i(TAG, "Backup time but one already running"); 898 } 899 } else { 900 Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned); 901 } 902 } 903 } 904 } 905 } 906 } 907 908 private class RunInitializeReceiver extends BroadcastReceiver { 909 public void onReceive(Context context, Intent intent) { 910 if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) { 911 synchronized (mQueueLock) { 912 if (DEBUG) Slog.v(TAG, "Running a device init"); 913 914 // Acquire the wakelock and pass it to the init thread. it will 915 // be released once init concludes. 916 mWakelock.acquire(); 917 918 Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE); 919 mBackupHandler.sendMessage(msg); 920 } 921 } 922 } 923 } 924 925 private void initPackageTracking() { 926 if (DEBUG) Slog.v(TAG, "Initializing package tracking"); 927 928 // Remember our ancestral dataset 929 mTokenFile = new File(mBaseStateDir, "ancestral"); 930 try { 931 RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r"); 932 int version = tf.readInt(); 933 if (version == CURRENT_ANCESTRAL_RECORD_VERSION) { 934 mAncestralToken = tf.readLong(); 935 mCurrentToken = tf.readLong(); 936 937 int numPackages = tf.readInt(); 938 if (numPackages >= 0) { 939 mAncestralPackages = new HashSet<String>(); 940 for (int i = 0; i < numPackages; i++) { 941 String pkgName = tf.readUTF(); 942 mAncestralPackages.add(pkgName); 943 } 944 } 945 } 946 tf.close(); 947 } catch (FileNotFoundException fnf) { 948 // Probably innocuous 949 Slog.v(TAG, "No ancestral data"); 950 } catch (IOException e) { 951 Slog.w(TAG, "Unable to read token file", e); 952 } 953 954 // Keep a log of what apps we've ever backed up. Because we might have 955 // rebooted in the middle of an operation that was removing something from 956 // this log, we sanity-check its contents here and reconstruct it. 957 mEverStored = new File(mBaseStateDir, "processed"); 958 File tempProcessedFile = new File(mBaseStateDir, "processed.new"); 959 960 // If we were in the middle of removing something from the ever-backed-up 961 // file, there might be a transient "processed.new" file still present. 962 // Ignore it -- we'll validate "processed" against the current package set. 963 if (tempProcessedFile.exists()) { 964 tempProcessedFile.delete(); 965 } 966 967 // If there are previous contents, parse them out then start a new 968 // file to continue the recordkeeping. 969 if (mEverStored.exists()) { 970 RandomAccessFile temp = null; 971 RandomAccessFile in = null; 972 973 try { 974 temp = new RandomAccessFile(tempProcessedFile, "rws"); 975 in = new RandomAccessFile(mEverStored, "r"); 976 977 while (true) { 978 PackageInfo info; 979 String pkg = in.readUTF(); 980 try { 981 info = mPackageManager.getPackageInfo(pkg, 0); 982 mEverStoredApps.add(pkg); 983 temp.writeUTF(pkg); 984 if (MORE_DEBUG) Slog.v(TAG, " + " + pkg); 985 } catch (NameNotFoundException e) { 986 // nope, this package was uninstalled; don't include it 987 if (MORE_DEBUG) Slog.v(TAG, " - " + pkg); 988 } 989 } 990 } catch (EOFException e) { 991 // Once we've rewritten the backup history log, atomically replace the 992 // old one with the new one then reopen the file for continuing use. 993 if (!tempProcessedFile.renameTo(mEverStored)) { 994 Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored); 995 } 996 } catch (IOException e) { 997 Slog.e(TAG, "Error in processed file", e); 998 } finally { 999 try { if (temp != null) temp.close(); } catch (IOException e) {} 1000 try { if (in != null) in.close(); } catch (IOException e) {} 1001 } 1002 } 1003 1004 // Register for broadcasts about package install, etc., so we can 1005 // update the provider list. 1006 IntentFilter filter = new IntentFilter(); 1007 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 1008 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1009 filter.addDataScheme("package"); 1010 mContext.registerReceiver(mBroadcastReceiver, filter); 1011 // Register for events related to sdcard installation. 1012 IntentFilter sdFilter = new IntentFilter(); 1013 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 1014 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 1015 mContext.registerReceiver(mBroadcastReceiver, sdFilter); 1016 } 1017 1018 private void parseLeftoverJournals() { 1019 for (File f : mJournalDir.listFiles()) { 1020 if (mJournal == null || f.compareTo(mJournal) != 0) { 1021 // This isn't the current journal, so it must be a leftover. Read 1022 // out the package names mentioned there and schedule them for 1023 // backup. 1024 RandomAccessFile in = null; 1025 try { 1026 Slog.i(TAG, "Found stale backup journal, scheduling"); 1027 in = new RandomAccessFile(f, "r"); 1028 while (true) { 1029 String packageName = in.readUTF(); 1030 Slog.i(TAG, " " + packageName); 1031 dataChangedImpl(packageName); 1032 } 1033 } catch (EOFException e) { 1034 // no more data; we're done 1035 } catch (Exception e) { 1036 Slog.e(TAG, "Can't read " + f, e); 1037 } finally { 1038 // close/delete the file 1039 try { if (in != null) in.close(); } catch (IOException e) {} 1040 f.delete(); 1041 } 1042 } 1043 } 1044 } 1045 1046 private SecretKey buildPasswordKey(String pw, byte[] salt, int rounds) { 1047 return buildCharArrayKey(pw.toCharArray(), salt, rounds); 1048 } 1049 1050 private SecretKey buildCharArrayKey(char[] pwArray, byte[] salt, int rounds) { 1051 try { 1052 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 1053 KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE); 1054 return keyFactory.generateSecret(ks); 1055 } catch (InvalidKeySpecException e) { 1056 Slog.e(TAG, "Invalid key spec for PBKDF2!"); 1057 } catch (NoSuchAlgorithmException e) { 1058 Slog.e(TAG, "PBKDF2 unavailable!"); 1059 } 1060 return null; 1061 } 1062 1063 private String buildPasswordHash(String pw, byte[] salt, int rounds) { 1064 SecretKey key = buildPasswordKey(pw, salt, rounds); 1065 if (key != null) { 1066 return byteArrayToHex(key.getEncoded()); 1067 } 1068 return null; 1069 } 1070 1071 private String byteArrayToHex(byte[] data) { 1072 StringBuilder buf = new StringBuilder(data.length * 2); 1073 for (int i = 0; i < data.length; i++) { 1074 buf.append(Byte.toHexString(data[i], true)); 1075 } 1076 return buf.toString(); 1077 } 1078 1079 private byte[] hexToByteArray(String digits) { 1080 final int bytes = digits.length() / 2; 1081 if (2*bytes != digits.length()) { 1082 throw new IllegalArgumentException("Hex string must have an even number of digits"); 1083 } 1084 1085 byte[] result = new byte[bytes]; 1086 for (int i = 0; i < digits.length(); i += 2) { 1087 result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16); 1088 } 1089 return result; 1090 } 1091 1092 private byte[] makeKeyChecksum(byte[] pwBytes, byte[] salt, int rounds) { 1093 char[] mkAsChar = new char[pwBytes.length]; 1094 for (int i = 0; i < pwBytes.length; i++) { 1095 mkAsChar[i] = (char) pwBytes[i]; 1096 } 1097 1098 Key checksum = buildCharArrayKey(mkAsChar, salt, rounds); 1099 return checksum.getEncoded(); 1100 } 1101 1102 // Used for generating random salts or passwords 1103 private byte[] randomBytes(int bits) { 1104 byte[] array = new byte[bits / 8]; 1105 mRng.nextBytes(array); 1106 return array; 1107 } 1108 1109 // Backup password management 1110 boolean passwordMatchesSaved(String candidatePw, int rounds) { 1111 // First, on an encrypted device we require matching the device pw 1112 final boolean isEncrypted; 1113 try { 1114 isEncrypted = (mMountService.getEncryptionState() != MountService.ENCRYPTION_STATE_NONE); 1115 if (isEncrypted) { 1116 if (DEBUG) { 1117 Slog.i(TAG, "Device encrypted; verifying against device data pw"); 1118 } 1119 // 0 means the password validated 1120 // -2 means device not encrypted 1121 // Any other result is either password failure or an error condition, 1122 // so we refuse the match 1123 final int result = mMountService.verifyEncryptionPassword(candidatePw); 1124 if (result == 0) { 1125 if (MORE_DEBUG) Slog.d(TAG, "Pw verifies"); 1126 return true; 1127 } else if (result != -2) { 1128 if (MORE_DEBUG) Slog.d(TAG, "Pw mismatch"); 1129 return false; 1130 } else { 1131 // ...else the device is supposedly not encrypted. HOWEVER, the 1132 // query about the encryption state said that the device *is* 1133 // encrypted, so ... we may have a problem. Log it and refuse 1134 // the backup. 1135 Slog.e(TAG, "verified encryption state mismatch against query; no match allowed"); 1136 return false; 1137 } 1138 } 1139 } catch (Exception e) { 1140 // Something went wrong talking to the mount service. This is very bad; 1141 // assume that we fail password validation. 1142 return false; 1143 } 1144 1145 if (mPasswordHash == null) { 1146 // no current password case -- require that 'currentPw' be null or empty 1147 if (candidatePw == null || "".equals(candidatePw)) { 1148 return true; 1149 } // else the non-empty candidate does not match the empty stored pw 1150 } else { 1151 // hash the stated current pw and compare to the stored one 1152 if (candidatePw != null && candidatePw.length() > 0) { 1153 String currentPwHash = buildPasswordHash(candidatePw, mPasswordSalt, rounds); 1154 if (mPasswordHash.equalsIgnoreCase(currentPwHash)) { 1155 // candidate hash matches the stored hash -- the password matches 1156 return true; 1157 } 1158 } // else the stored pw is nonempty but the candidate is empty; no match 1159 } 1160 return false; 1161 } 1162 1163 @Override 1164 public boolean setBackupPassword(String currentPw, String newPw) { 1165 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 1166 "setBackupPassword"); 1167 1168 // If the supplied pw doesn't hash to the the saved one, fail 1169 if (!passwordMatchesSaved(currentPw, PBKDF2_HASH_ROUNDS)) { 1170 return false; 1171 } 1172 1173 // Clearing the password is okay 1174 if (newPw == null || newPw.isEmpty()) { 1175 if (mPasswordHashFile.exists()) { 1176 if (!mPasswordHashFile.delete()) { 1177 // Unable to delete the old pw file, so fail 1178 Slog.e(TAG, "Unable to clear backup password"); 1179 return false; 1180 } 1181 } 1182 mPasswordHash = null; 1183 mPasswordSalt = null; 1184 return true; 1185 } 1186 1187 try { 1188 // Okay, build the hash of the new backup password 1189 byte[] salt = randomBytes(PBKDF2_SALT_SIZE); 1190 String newPwHash = buildPasswordHash(newPw, salt, PBKDF2_HASH_ROUNDS); 1191 1192 OutputStream pwf = null, buffer = null; 1193 DataOutputStream out = null; 1194 try { 1195 pwf = new FileOutputStream(mPasswordHashFile); 1196 buffer = new BufferedOutputStream(pwf); 1197 out = new DataOutputStream(buffer); 1198 // integer length of the salt array, followed by the salt, 1199 // then the hex pw hash string 1200 out.writeInt(salt.length); 1201 out.write(salt); 1202 out.writeUTF(newPwHash); 1203 out.flush(); 1204 mPasswordHash = newPwHash; 1205 mPasswordSalt = salt; 1206 return true; 1207 } finally { 1208 if (out != null) out.close(); 1209 if (buffer != null) buffer.close(); 1210 if (pwf != null) pwf.close(); 1211 } 1212 } catch (IOException e) { 1213 Slog.e(TAG, "Unable to set backup password"); 1214 } 1215 return false; 1216 } 1217 1218 @Override 1219 public boolean hasBackupPassword() { 1220 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 1221 "hasBackupPassword"); 1222 1223 try { 1224 return (mMountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE) 1225 || (mPasswordHash != null && mPasswordHash.length() > 0); 1226 } catch (Exception e) { 1227 // If we can't talk to the mount service we have a serious problem; fail 1228 // "secure" i.e. assuming that we require a password 1229 return true; 1230 } 1231 } 1232 1233 // Maintain persistent state around whether need to do an initialize operation. 1234 // Must be called with the queue lock held. 1235 void recordInitPendingLocked(boolean isPending, String transportName) { 1236 if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending 1237 + " on transport " + transportName); 1238 try { 1239 IBackupTransport transport = getTransport(transportName); 1240 String transportDirName = transport.transportDirName(); 1241 File stateDir = new File(mBaseStateDir, transportDirName); 1242 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME); 1243 1244 if (isPending) { 1245 // We need an init before we can proceed with sending backup data. 1246 // Record that with an entry in our set of pending inits, as well as 1247 // journaling it via creation of a sentinel file. 1248 mPendingInits.add(transportName); 1249 try { 1250 (new FileOutputStream(initPendingFile)).close(); 1251 } catch (IOException ioe) { 1252 // Something is badly wrong with our permissions; just try to move on 1253 } 1254 } else { 1255 // No more initialization needed; wipe the journal and reset our state. 1256 initPendingFile.delete(); 1257 mPendingInits.remove(transportName); 1258 } 1259 } catch (RemoteException e) { 1260 // can't happen; the transport is local 1261 } 1262 } 1263 1264 // Reset all of our bookkeeping, in response to having been told that 1265 // the backend data has been wiped [due to idle expiry, for example], 1266 // so we must re-upload all saved settings. 1267 void resetBackupState(File stateFileDir) { 1268 synchronized (mQueueLock) { 1269 // Wipe the "what we've ever backed up" tracking 1270 mEverStoredApps.clear(); 1271 mEverStored.delete(); 1272 1273 mCurrentToken = 0; 1274 writeRestoreTokens(); 1275 1276 // Remove all the state files 1277 for (File sf : stateFileDir.listFiles()) { 1278 // ... but don't touch the needs-init sentinel 1279 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) { 1280 sf.delete(); 1281 } 1282 } 1283 } 1284 1285 // Enqueue a new backup of every participant 1286 synchronized (mBackupParticipants) { 1287 final int N = mBackupParticipants.size(); 1288 for (int i=0; i<N; i++) { 1289 HashSet<String> participants = mBackupParticipants.valueAt(i); 1290 if (participants != null) { 1291 for (String packageName : participants) { 1292 dataChangedImpl(packageName); 1293 } 1294 } 1295 } 1296 } 1297 } 1298 1299 // Add a transport to our set of available backends. If 'transport' is null, this 1300 // is an unregistration, and the transport's entry is removed from our bookkeeping. 1301 private void registerTransport(String name, IBackupTransport transport) { 1302 synchronized (mTransports) { 1303 if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport); 1304 if (transport != null) { 1305 mTransports.put(name, transport); 1306 } else { 1307 mTransports.remove(name); 1308 if ((mCurrentTransport != null) && mCurrentTransport.equals(name)) { 1309 mCurrentTransport = null; 1310 } 1311 // Nothing further to do in the unregistration case 1312 return; 1313 } 1314 } 1315 1316 // If the init sentinel file exists, we need to be sure to perform the init 1317 // as soon as practical. We also create the state directory at registration 1318 // time to ensure it's present from the outset. 1319 try { 1320 String transportName = transport.transportDirName(); 1321 File stateDir = new File(mBaseStateDir, transportName); 1322 stateDir.mkdirs(); 1323 1324 File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME); 1325 if (initSentinel.exists()) { 1326 synchronized (mQueueLock) { 1327 mPendingInits.add(transportName); 1328 1329 // TODO: pick a better starting time than now + 1 minute 1330 long delay = 1000 * 60; // one minute, in milliseconds 1331 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 1332 System.currentTimeMillis() + delay, mRunInitIntent); 1333 } 1334 } 1335 } catch (RemoteException e) { 1336 // can't happen, the transport is local 1337 } 1338 } 1339 1340 // ----- Track installation/removal of packages ----- 1341 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1342 public void onReceive(Context context, Intent intent) { 1343 if (DEBUG) Slog.d(TAG, "Received broadcast " + intent); 1344 1345 String action = intent.getAction(); 1346 boolean replacing = false; 1347 boolean added = false; 1348 Bundle extras = intent.getExtras(); 1349 String pkgList[] = null; 1350 if (Intent.ACTION_PACKAGE_ADDED.equals(action) || 1351 Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 1352 Uri uri = intent.getData(); 1353 if (uri == null) { 1354 return; 1355 } 1356 String pkgName = uri.getSchemeSpecificPart(); 1357 if (pkgName != null) { 1358 pkgList = new String[] { pkgName }; 1359 } 1360 added = Intent.ACTION_PACKAGE_ADDED.equals(action); 1361 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); 1362 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { 1363 added = true; 1364 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1365 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { 1366 added = false; 1367 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1368 } 1369 1370 if (pkgList == null || pkgList.length == 0) { 1371 return; 1372 } 1373 1374 final int uid = extras.getInt(Intent.EXTRA_UID); 1375 if (added) { 1376 synchronized (mBackupParticipants) { 1377 if (replacing) { 1378 // This is the package-replaced case; we just remove the entry 1379 // under the old uid and fall through to re-add. 1380 removePackageParticipantsLocked(pkgList, uid); 1381 } 1382 addPackageParticipantsLocked(pkgList); 1383 } 1384 } else { 1385 if (replacing) { 1386 // The package is being updated. We'll receive a PACKAGE_ADDED shortly. 1387 } else { 1388 synchronized (mBackupParticipants) { 1389 removePackageParticipantsLocked(pkgList, uid); 1390 } 1391 } 1392 } 1393 } 1394 }; 1395 1396 // ----- Track connection to GoogleBackupTransport service ----- 1397 ServiceConnection mGoogleConnection = new ServiceConnection() { 1398 public void onServiceConnected(ComponentName name, IBinder service) { 1399 if (DEBUG) Slog.v(TAG, "Connected to Google transport"); 1400 mGoogleTransport = IBackupTransport.Stub.asInterface(service); 1401 registerTransport(name.flattenToShortString(), mGoogleTransport); 1402 } 1403 1404 public void onServiceDisconnected(ComponentName name) { 1405 if (DEBUG) Slog.v(TAG, "Disconnected from Google transport"); 1406 mGoogleTransport = null; 1407 registerTransport(name.flattenToShortString(), null); 1408 } 1409 }; 1410 1411 // Add the backup agents in the given packages to our set of known backup participants. 1412 // If 'packageNames' is null, adds all backup agents in the whole system. 1413 void addPackageParticipantsLocked(String[] packageNames) { 1414 // Look for apps that define the android:backupAgent attribute 1415 List<PackageInfo> targetApps = allAgentPackages(); 1416 if (packageNames != null) { 1417 if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length); 1418 for (String packageName : packageNames) { 1419 addPackageParticipantsLockedInner(packageName, targetApps); 1420 } 1421 } else { 1422 if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all"); 1423 addPackageParticipantsLockedInner(null, targetApps); 1424 } 1425 } 1426 1427 private void addPackageParticipantsLockedInner(String packageName, 1428 List<PackageInfo> targetPkgs) { 1429 if (MORE_DEBUG) { 1430 Slog.v(TAG, "Examining " + packageName + " for backup agent"); 1431 } 1432 1433 for (PackageInfo pkg : targetPkgs) { 1434 if (packageName == null || pkg.packageName.equals(packageName)) { 1435 int uid = pkg.applicationInfo.uid; 1436 HashSet<String> set = mBackupParticipants.get(uid); 1437 if (set == null) { 1438 set = new HashSet<String>(); 1439 mBackupParticipants.put(uid, set); 1440 } 1441 set.add(pkg.packageName); 1442 if (MORE_DEBUG) Slog.v(TAG, "Agent found; added"); 1443 1444 // Schedule a backup for it on general principles 1445 if (DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName); 1446 dataChangedImpl(pkg.packageName); 1447 } 1448 } 1449 } 1450 1451 // Remove the given packages' entries from our known active set. 1452 void removePackageParticipantsLocked(String[] packageNames, int oldUid) { 1453 if (packageNames == null) { 1454 Slog.w(TAG, "removePackageParticipants with null list"); 1455 return; 1456 } 1457 1458 if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid 1459 + " #" + packageNames.length); 1460 for (String pkg : packageNames) { 1461 // Known previous UID, so we know which package set to check 1462 HashSet<String> set = mBackupParticipants.get(oldUid); 1463 if (set != null && set.contains(pkg)) { 1464 removePackageFromSetLocked(set, pkg); 1465 if (set.isEmpty()) { 1466 if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set"); 1467 mBackupParticipants.remove(oldUid); 1468 } 1469 } 1470 } 1471 } 1472 1473 private void removePackageFromSetLocked(final HashSet<String> set, 1474 final String packageName) { 1475 if (set.contains(packageName)) { 1476 // Found it. Remove this one package from the bookkeeping, and 1477 // if it's the last participating app under this uid we drop the 1478 // (now-empty) set as well. 1479 // Note that we deliberately leave it 'known' in the "ever backed up" 1480 // bookkeeping so that its current-dataset data will be retrieved 1481 // if the app is subsequently reinstalled 1482 if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName); 1483 set.remove(packageName); 1484 mPendingBackups.remove(packageName); 1485 } 1486 } 1487 1488 // Returns the set of all applications that define an android:backupAgent attribute 1489 List<PackageInfo> allAgentPackages() { 1490 // !!! TODO: cache this and regenerate only when necessary 1491 int flags = PackageManager.GET_SIGNATURES; 1492 List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags); 1493 int N = packages.size(); 1494 for (int a = N-1; a >= 0; a--) { 1495 PackageInfo pkg = packages.get(a); 1496 try { 1497 ApplicationInfo app = pkg.applicationInfo; 1498 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) 1499 || app.backupAgentName == null) { 1500 packages.remove(a); 1501 } 1502 else { 1503 // we will need the shared library path, so look that up and store it here 1504 app = mPackageManager.getApplicationInfo(pkg.packageName, 1505 PackageManager.GET_SHARED_LIBRARY_FILES); 1506 pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles; 1507 } 1508 } catch (NameNotFoundException e) { 1509 packages.remove(a); 1510 } 1511 } 1512 return packages; 1513 } 1514 1515 // Called from the backup task: record that the given app has been successfully 1516 // backed up at least once 1517 void logBackupComplete(String packageName) { 1518 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return; 1519 1520 synchronized (mEverStoredApps) { 1521 if (!mEverStoredApps.add(packageName)) return; 1522 1523 RandomAccessFile out = null; 1524 try { 1525 out = new RandomAccessFile(mEverStored, "rws"); 1526 out.seek(out.length()); 1527 out.writeUTF(packageName); 1528 } catch (IOException e) { 1529 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored); 1530 } finally { 1531 try { if (out != null) out.close(); } catch (IOException e) {} 1532 } 1533 } 1534 } 1535 1536 // Remove our awareness of having ever backed up the given package 1537 void removeEverBackedUp(String packageName) { 1538 if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName); 1539 if (MORE_DEBUG) Slog.v(TAG, "New set:"); 1540 1541 synchronized (mEverStoredApps) { 1542 // Rewrite the file and rename to overwrite. If we reboot in the middle, 1543 // we'll recognize on initialization time that the package no longer 1544 // exists and fix it up then. 1545 File tempKnownFile = new File(mBaseStateDir, "processed.new"); 1546 RandomAccessFile known = null; 1547 try { 1548 known = new RandomAccessFile(tempKnownFile, "rws"); 1549 mEverStoredApps.remove(packageName); 1550 for (String s : mEverStoredApps) { 1551 known.writeUTF(s); 1552 if (MORE_DEBUG) Slog.v(TAG, " " + s); 1553 } 1554 known.close(); 1555 known = null; 1556 if (!tempKnownFile.renameTo(mEverStored)) { 1557 throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored); 1558 } 1559 } catch (IOException e) { 1560 // Bad: we couldn't create the new copy. For safety's sake we 1561 // abandon the whole process and remove all what's-backed-up 1562 // state entirely, meaning we'll force a backup pass for every 1563 // participant on the next boot or [re]install. 1564 Slog.w(TAG, "Error rewriting " + mEverStored, e); 1565 mEverStoredApps.clear(); 1566 tempKnownFile.delete(); 1567 mEverStored.delete(); 1568 } finally { 1569 try { if (known != null) known.close(); } catch (IOException e) {} 1570 } 1571 } 1572 } 1573 1574 // Persistently record the current and ancestral backup tokens as well 1575 // as the set of packages with data [supposedly] available in the 1576 // ancestral dataset. 1577 void writeRestoreTokens() { 1578 try { 1579 RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd"); 1580 1581 // First, the version number of this record, for futureproofing 1582 af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION); 1583 1584 // Write the ancestral and current tokens 1585 af.writeLong(mAncestralToken); 1586 af.writeLong(mCurrentToken); 1587 1588 // Now write the set of ancestral packages 1589 if (mAncestralPackages == null) { 1590 af.writeInt(-1); 1591 } else { 1592 af.writeInt(mAncestralPackages.size()); 1593 if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size()); 1594 for (String pkgName : mAncestralPackages) { 1595 af.writeUTF(pkgName); 1596 if (MORE_DEBUG) Slog.v(TAG, " " + pkgName); 1597 } 1598 } 1599 af.close(); 1600 } catch (IOException e) { 1601 Slog.w(TAG, "Unable to write token file:", e); 1602 } 1603 } 1604 1605 // Return the given transport 1606 private IBackupTransport getTransport(String transportName) { 1607 synchronized (mTransports) { 1608 IBackupTransport transport = mTransports.get(transportName); 1609 if (transport == null) { 1610 Slog.w(TAG, "Requested unavailable transport: " + transportName); 1611 } 1612 return transport; 1613 } 1614 } 1615 1616 // fire off a backup agent, blocking until it attaches or times out 1617 IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) { 1618 IBackupAgent agent = null; 1619 synchronized(mAgentConnectLock) { 1620 mConnecting = true; 1621 mConnectedAgent = null; 1622 try { 1623 if (mActivityManager.bindBackupAgent(app, mode)) { 1624 Slog.d(TAG, "awaiting agent for " + app); 1625 1626 // success; wait for the agent to arrive 1627 // only wait 10 seconds for the bind to happen 1628 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; 1629 while (mConnecting && mConnectedAgent == null 1630 && (System.currentTimeMillis() < timeoutMark)) { 1631 try { 1632 mAgentConnectLock.wait(5000); 1633 } catch (InterruptedException e) { 1634 // just bail 1635 if (DEBUG) Slog.w(TAG, "Interrupted: " + e); 1636 mActivityManager.clearPendingBackup(); 1637 return null; 1638 } 1639 } 1640 1641 // if we timed out with no connect, abort and move on 1642 if (mConnecting == true) { 1643 Slog.w(TAG, "Timeout waiting for agent " + app); 1644 mActivityManager.clearPendingBackup(); 1645 return null; 1646 } 1647 if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent); 1648 agent = mConnectedAgent; 1649 } 1650 } catch (RemoteException e) { 1651 // can't happen 1652 } 1653 } 1654 return agent; 1655 } 1656 1657 // clear an application's data, blocking until the operation completes or times out 1658 void clearApplicationDataSynchronous(String packageName) { 1659 // Don't wipe packages marked allowClearUserData=false 1660 try { 1661 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); 1662 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) { 1663 if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping " 1664 + packageName); 1665 return; 1666 } 1667 } catch (NameNotFoundException e) { 1668 Slog.w(TAG, "Tried to clear data for " + packageName + " but not found"); 1669 return; 1670 } 1671 1672 ClearDataObserver observer = new ClearDataObserver(); 1673 1674 synchronized(mClearDataLock) { 1675 mClearingData = true; 1676 try { 1677 mActivityManager.clearApplicationUserData(packageName, observer, 0); 1678 } catch (RemoteException e) { 1679 // can't happen because the activity manager is in this process 1680 } 1681 1682 // only wait 10 seconds for the clear data to happen 1683 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; 1684 while (mClearingData && (System.currentTimeMillis() < timeoutMark)) { 1685 try { 1686 mClearDataLock.wait(5000); 1687 } catch (InterruptedException e) { 1688 // won't happen, but still. 1689 mClearingData = false; 1690 } 1691 } 1692 } 1693 } 1694 1695 class ClearDataObserver extends IPackageDataObserver.Stub { 1696 public void onRemoveCompleted(String packageName, boolean succeeded) { 1697 synchronized(mClearDataLock) { 1698 mClearingData = false; 1699 mClearDataLock.notifyAll(); 1700 } 1701 } 1702 } 1703 1704 // Get the restore-set token for the best-available restore set for this package: 1705 // the active set if possible, else the ancestral one. Returns zero if none available. 1706 long getAvailableRestoreToken(String packageName) { 1707 long token = mAncestralToken; 1708 synchronized (mQueueLock) { 1709 if (mEverStoredApps.contains(packageName)) { 1710 token = mCurrentToken; 1711 } 1712 } 1713 return token; 1714 } 1715 1716 // ----- 1717 // Interface and methods used by the asynchronous-with-timeout backup/restore operations 1718 1719 interface BackupRestoreTask { 1720 // Execute one tick of whatever state machine the task implements 1721 void execute(); 1722 1723 // An operation that wanted a callback has completed 1724 void operationComplete(); 1725 1726 // An operation that wanted a callback has timed out 1727 void handleTimeout(); 1728 } 1729 1730 void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) { 1731 if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token) 1732 + " interval=" + interval); 1733 synchronized (mCurrentOpLock) { 1734 mCurrentOperations.put(token, new Operation(OP_PENDING, callback)); 1735 1736 Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback); 1737 mBackupHandler.sendMessageDelayed(msg, interval); 1738 } 1739 } 1740 1741 // synchronous waiter case 1742 boolean waitUntilOperationComplete(int token) { 1743 if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for " 1744 + Integer.toHexString(token)); 1745 int finalState = OP_PENDING; 1746 Operation op = null; 1747 synchronized (mCurrentOpLock) { 1748 while (true) { 1749 op = mCurrentOperations.get(token); 1750 if (op == null) { 1751 // mysterious disappearance: treat as success with no callback 1752 break; 1753 } else { 1754 if (op.state == OP_PENDING) { 1755 try { 1756 mCurrentOpLock.wait(); 1757 } catch (InterruptedException e) {} 1758 // When the wait is notified we loop around and recheck the current state 1759 } else { 1760 // No longer pending; we're done 1761 finalState = op.state; 1762 break; 1763 } 1764 } 1765 } 1766 } 1767 1768 mBackupHandler.removeMessages(MSG_TIMEOUT); 1769 if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token) 1770 + " complete: finalState=" + finalState); 1771 return finalState == OP_ACKNOWLEDGED; 1772 } 1773 1774 void handleTimeout(int token, Object obj) { 1775 // Notify any synchronous waiters 1776 Operation op = null; 1777 synchronized (mCurrentOpLock) { 1778 op = mCurrentOperations.get(token); 1779 if (MORE_DEBUG) { 1780 if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token) 1781 + " but no op found"); 1782 } 1783 int state = (op != null) ? op.state : OP_TIMEOUT; 1784 if (state == OP_PENDING) { 1785 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token)); 1786 op.state = OP_TIMEOUT; 1787 mCurrentOperations.put(token, op); 1788 } 1789 mCurrentOpLock.notifyAll(); 1790 } 1791 1792 // If there's a TimeoutHandler for this event, call it 1793 if (op != null && op.callback != null) { 1794 op.callback.handleTimeout(); 1795 } 1796 } 1797 1798 // ----- Back up a set of applications via a worker thread ----- 1799 1800 enum BackupState { 1801 INITIAL, 1802 RUNNING_QUEUE, 1803 FINAL 1804 } 1805 1806 class PerformBackupTask implements BackupRestoreTask { 1807 private static final String TAG = "PerformBackupTask"; 1808 1809 IBackupTransport mTransport; 1810 ArrayList<BackupRequest> mQueue; 1811 ArrayList<BackupRequest> mOriginalQueue; 1812 File mStateDir; 1813 File mJournal; 1814 BackupState mCurrentState; 1815 1816 // carried information about the current in-flight operation 1817 PackageInfo mCurrentPackage; 1818 File mSavedStateName; 1819 File mBackupDataName; 1820 File mNewStateName; 1821 ParcelFileDescriptor mSavedState; 1822 ParcelFileDescriptor mBackupData; 1823 ParcelFileDescriptor mNewState; 1824 int mStatus; 1825 boolean mFinished; 1826 1827 public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue, 1828 File journal) { 1829 mTransport = transport; 1830 mOriginalQueue = queue; 1831 mJournal = journal; 1832 1833 try { 1834 mStateDir = new File(mBaseStateDir, transport.transportDirName()); 1835 } catch (RemoteException e) { 1836 // can't happen; the transport is local 1837 } 1838 1839 mCurrentState = BackupState.INITIAL; 1840 mFinished = false; 1841 1842 addBackupTrace("STATE => INITIAL"); 1843 } 1844 1845 // Main entry point: perform one chunk of work, updating the state as appropriate 1846 // and reposting the next chunk to the primary backup handler thread. 1847 @Override 1848 public void execute() { 1849 switch (mCurrentState) { 1850 case INITIAL: 1851 beginBackup(); 1852 break; 1853 1854 case RUNNING_QUEUE: 1855 invokeNextAgent(); 1856 break; 1857 1858 case FINAL: 1859 if (!mFinished) finalizeBackup(); 1860 else { 1861 Slog.e(TAG, "Duplicate finish"); 1862 } 1863 mFinished = true; 1864 break; 1865 } 1866 } 1867 1868 // We're starting a backup pass. Initialize the transport and send 1869 // the PM metadata blob if we haven't already. 1870 void beginBackup() { 1871 if (DEBUG_BACKUP_TRACE) { 1872 clearBackupTrace(); 1873 StringBuilder b = new StringBuilder(256); 1874 b.append("beginBackup: ["); 1875 for (BackupRequest req : mOriginalQueue) { 1876 b.append(' '); 1877 b.append(req.packageName); 1878 } 1879 b.append(" ]"); 1880 addBackupTrace(b.toString()); 1881 } 1882 1883 mStatus = BackupConstants.TRANSPORT_OK; 1884 1885 // Sanity check: if the queue is empty we have no work to do. 1886 if (mOriginalQueue.isEmpty()) { 1887 Slog.w(TAG, "Backup begun with an empty queue - nothing to do."); 1888 addBackupTrace("queue empty at begin"); 1889 executeNextState(BackupState.FINAL); 1890 return; 1891 } 1892 1893 // We need to retain the original queue contents in case of transport 1894 // failure, but we want a working copy that we can manipulate along 1895 // the way. 1896 mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone(); 1897 1898 if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); 1899 1900 File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); 1901 try { 1902 final String transportName = mTransport.transportDirName(); 1903 EventLog.writeEvent(EventLogTags.BACKUP_START, transportName); 1904 1905 // If we haven't stored package manager metadata yet, we must init the transport. 1906 if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) { 1907 Slog.i(TAG, "Initializing (wiping) backup state and transport storage"); 1908 addBackupTrace("initializing transport " + transportName); 1909 resetBackupState(mStateDir); // Just to make sure. 1910 mStatus = mTransport.initializeDevice(); 1911 1912 addBackupTrace("transport.initializeDevice() == " + mStatus); 1913 if (mStatus == BackupConstants.TRANSPORT_OK) { 1914 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 1915 } else { 1916 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 1917 Slog.e(TAG, "Transport error in initializeDevice()"); 1918 } 1919 } 1920 1921 // The package manager doesn't have a proper <application> etc, but since 1922 // it's running here in the system process we can just set up its agent 1923 // directly and use a synthetic BackupRequest. We always run this pass 1924 // because it's cheap and this way we guarantee that we don't get out of 1925 // step even if we're selecting among various transports at run time. 1926 if (mStatus == BackupConstants.TRANSPORT_OK) { 1927 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( 1928 mPackageManager, allAgentPackages()); 1929 mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL, 1930 IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); 1931 addBackupTrace("PMBA invoke: " + mStatus); 1932 } 1933 1934 if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) { 1935 // The backend reports that our dataset has been wiped. Note this in 1936 // the event log; the no-success code below will reset the backup 1937 // state as well. 1938 EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName()); 1939 } 1940 } catch (Exception e) { 1941 Slog.e(TAG, "Error in backup thread", e); 1942 addBackupTrace("Exception in backup thread: " + e); 1943 mStatus = BackupConstants.TRANSPORT_ERROR; 1944 } finally { 1945 // If we've succeeded so far, invokeAgentForBackup() will have run the PM 1946 // metadata and its completion/timeout callback will continue the state 1947 // machine chain. If it failed that won't happen; we handle that now. 1948 addBackupTrace("exiting prelim: " + mStatus); 1949 if (mStatus != BackupConstants.TRANSPORT_OK) { 1950 // if things went wrong at this point, we need to 1951 // restage everything and try again later. 1952 resetBackupState(mStateDir); // Just to make sure. 1953 executeNextState(BackupState.FINAL); 1954 } 1955 } 1956 } 1957 1958 // Transport has been initialized and the PM metadata submitted successfully 1959 // if that was warranted. Now we process the single next thing in the queue. 1960 void invokeNextAgent() { 1961 mStatus = BackupConstants.TRANSPORT_OK; 1962 addBackupTrace("invoke q=" + mQueue.size()); 1963 1964 // Sanity check that we have work to do. If not, skip to the end where 1965 // we reestablish the wakelock invariants etc. 1966 if (mQueue.isEmpty()) { 1967 if (DEBUG) Slog.i(TAG, "queue now empty"); 1968 executeNextState(BackupState.FINAL); 1969 return; 1970 } 1971 1972 // pop the entry we're going to process on this step 1973 BackupRequest request = mQueue.get(0); 1974 mQueue.remove(0); 1975 1976 Slog.d(TAG, "starting agent for backup of " + request); 1977 addBackupTrace("launch agent for " + request.packageName); 1978 1979 // Verify that the requested app exists; it might be something that 1980 // requested a backup but was then uninstalled. The request was 1981 // journalled and rather than tamper with the journal it's safer 1982 // to sanity-check here. This also gives us the classname of the 1983 // package's backup agent. 1984 try { 1985 mCurrentPackage = mPackageManager.getPackageInfo(request.packageName, 1986 PackageManager.GET_SIGNATURES); 1987 if (mCurrentPackage.applicationInfo.backupAgentName == null) { 1988 // The manifest has changed but we had a stale backup request pending. 1989 // This won't happen again because the app won't be requesting further 1990 // backups. 1991 Slog.i(TAG, "Package " + request.packageName 1992 + " no longer supports backup; skipping"); 1993 addBackupTrace("skipping - no agent, completion is noop"); 1994 executeNextState(BackupState.RUNNING_QUEUE); 1995 return; 1996 } 1997 1998 IBackupAgent agent = null; 1999 try { 2000 mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid)); 2001 agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo, 2002 IApplicationThread.BACKUP_MODE_INCREMENTAL); 2003 addBackupTrace("agent bound; a? = " + (agent != null)); 2004 if (agent != null) { 2005 mStatus = invokeAgentForBackup(request.packageName, agent, mTransport); 2006 // at this point we'll either get a completion callback from the 2007 // agent, or a timeout message on the main handler. either way, we're 2008 // done here as long as we're successful so far. 2009 } else { 2010 // Timeout waiting for the agent 2011 mStatus = BackupConstants.AGENT_ERROR; 2012 } 2013 } catch (SecurityException ex) { 2014 // Try for the next one. 2015 Slog.d(TAG, "error in bind/backup", ex); 2016 mStatus = BackupConstants.AGENT_ERROR; 2017 addBackupTrace("agent SE"); 2018 } 2019 } catch (NameNotFoundException e) { 2020 Slog.d(TAG, "Package does not exist; skipping"); 2021 addBackupTrace("no such package"); 2022 mStatus = BackupConstants.AGENT_UNKNOWN; 2023 } finally { 2024 mWakelock.setWorkSource(null); 2025 2026 // If there was an agent error, no timeout/completion handling will occur. 2027 // That means we need to direct to the next state ourselves. 2028 if (mStatus != BackupConstants.TRANSPORT_OK) { 2029 BackupState nextState = BackupState.RUNNING_QUEUE; 2030 2031 // An agent-level failure means we reenqueue this one agent for 2032 // a later retry, but otherwise proceed normally. 2033 if (mStatus == BackupConstants.AGENT_ERROR) { 2034 if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName 2035 + " - restaging"); 2036 dataChangedImpl(request.packageName); 2037 mStatus = BackupConstants.TRANSPORT_OK; 2038 if (mQueue.isEmpty()) nextState = BackupState.FINAL; 2039 } else if (mStatus == BackupConstants.AGENT_UNKNOWN) { 2040 // Failed lookup of the app, so we couldn't bring up an agent, but 2041 // we're otherwise fine. Just drop it and go on to the next as usual. 2042 mStatus = BackupConstants.TRANSPORT_OK; 2043 } else { 2044 // Transport-level failure means we reenqueue everything 2045 revertAndEndBackup(); 2046 nextState = BackupState.FINAL; 2047 } 2048 2049 executeNextState(nextState); 2050 } else { 2051 addBackupTrace("expecting completion/timeout callback"); 2052 } 2053 } 2054 } 2055 2056 void finalizeBackup() { 2057 addBackupTrace("finishing"); 2058 2059 // Either backup was successful, in which case we of course do not need 2060 // this pass's journal any more; or it failed, in which case we just 2061 // re-enqueued all of these packages in the current active journal. 2062 // Either way, we no longer need this pass's journal. 2063 if (mJournal != null && !mJournal.delete()) { 2064 Slog.e(TAG, "Unable to remove backup journal file " + mJournal); 2065 } 2066 2067 // If everything actually went through and this is the first time we've 2068 // done a backup, we can now record what the current backup dataset token 2069 // is. 2070 if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) { 2071 addBackupTrace("success; recording token"); 2072 try { 2073 mCurrentToken = mTransport.getCurrentRestoreSet(); 2074 } catch (RemoteException e) {} // can't happen 2075 writeRestoreTokens(); 2076 } 2077 2078 // Set up the next backup pass - at this point we can set mBackupRunning 2079 // to false to allow another pass to fire, because we're done with the 2080 // state machine sequence and the wakelock is refcounted. 2081 synchronized (mQueueLock) { 2082 mBackupRunning = false; 2083 if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) { 2084 // Make sure we back up everything and perform the one-time init 2085 clearMetadata(); 2086 if (DEBUG) Slog.d(TAG, "Server requires init; rerunning"); 2087 addBackupTrace("init required; rerunning"); 2088 backupNow(); 2089 } 2090 } 2091 2092 // Only once we're entirely finished do we release the wakelock 2093 clearBackupTrace(); 2094 Slog.i(TAG, "Backup pass finished."); 2095 mWakelock.release(); 2096 } 2097 2098 // Remove the PM metadata state. This will generate an init on the next pass. 2099 void clearMetadata() { 2100 final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); 2101 if (pmState.exists()) pmState.delete(); 2102 } 2103 2104 // Invoke an agent's doBackup() and start a timeout message spinning on the main 2105 // handler in case it doesn't get back to us. 2106 int invokeAgentForBackup(String packageName, IBackupAgent agent, 2107 IBackupTransport transport) { 2108 if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName); 2109 addBackupTrace("invoking " + packageName); 2110 2111 mSavedStateName = new File(mStateDir, packageName); 2112 mBackupDataName = new File(mDataDir, packageName + ".data"); 2113 mNewStateName = new File(mStateDir, packageName + ".new"); 2114 2115 mSavedState = null; 2116 mBackupData = null; 2117 mNewState = null; 2118 2119 final int token = generateToken(); 2120 try { 2121 // Look up the package info & signatures. This is first so that if it 2122 // throws an exception, there's no file setup yet that would need to 2123 // be unraveled. 2124 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) { 2125 // The metadata 'package' is synthetic; construct one and make 2126 // sure our global state is pointed at it 2127 mCurrentPackage = new PackageInfo(); 2128 mCurrentPackage.packageName = packageName; 2129 } 2130 2131 // In a full backup, we pass a null ParcelFileDescriptor as 2132 // the saved-state "file". This is by definition an incremental, 2133 // so we build a saved state file to pass. 2134 mSavedState = ParcelFileDescriptor.open(mSavedStateName, 2135 ParcelFileDescriptor.MODE_READ_ONLY | 2136 ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary 2137 2138 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 2139 ParcelFileDescriptor.MODE_READ_WRITE | 2140 ParcelFileDescriptor.MODE_CREATE | 2141 ParcelFileDescriptor.MODE_TRUNCATE); 2142 2143 if (!SELinux.restorecon(mBackupDataName)) { 2144 Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName); 2145 } 2146 2147 mNewState = ParcelFileDescriptor.open(mNewStateName, 2148 ParcelFileDescriptor.MODE_READ_WRITE | 2149 ParcelFileDescriptor.MODE_CREATE | 2150 ParcelFileDescriptor.MODE_TRUNCATE); 2151 2152 // Initiate the target's backup pass 2153 addBackupTrace("setting timeout"); 2154 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this); 2155 addBackupTrace("calling agent doBackup()"); 2156 agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder); 2157 } catch (Exception e) { 2158 Slog.e(TAG, "Error invoking for backup on " + packageName); 2159 addBackupTrace("exception: " + e); 2160 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, 2161 e.toString()); 2162 agentErrorCleanup(); 2163 return BackupConstants.AGENT_ERROR; 2164 } 2165 2166 // At this point the agent is off and running. The next thing to happen will 2167 // either be a callback from the agent, at which point we'll process its data 2168 // for transport, or a timeout. Either way the next phase will happen in 2169 // response to the TimeoutHandler interface callbacks. 2170 addBackupTrace("invoke success"); 2171 return BackupConstants.TRANSPORT_OK; 2172 } 2173 2174 @Override 2175 public void operationComplete() { 2176 // Okay, the agent successfully reported back to us. Spin the data off to the 2177 // transport and proceed with the next stage. 2178 if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for " 2179 + mCurrentPackage.packageName); 2180 mBackupHandler.removeMessages(MSG_TIMEOUT); 2181 clearAgentState(); 2182 addBackupTrace("operation complete"); 2183 2184 ParcelFileDescriptor backupData = null; 2185 mStatus = BackupConstants.TRANSPORT_OK; 2186 try { 2187 int size = (int) mBackupDataName.length(); 2188 if (size > 0) { 2189 if (mStatus == BackupConstants.TRANSPORT_OK) { 2190 backupData = ParcelFileDescriptor.open(mBackupDataName, 2191 ParcelFileDescriptor.MODE_READ_ONLY); 2192 addBackupTrace("sending data to transport"); 2193 mStatus = mTransport.performBackup(mCurrentPackage, backupData); 2194 } 2195 2196 // TODO - We call finishBackup() for each application backed up, because 2197 // we need to know now whether it succeeded or failed. Instead, we should 2198 // hold off on finishBackup() until the end, which implies holding off on 2199 // renaming *all* the output state files (see below) until that happens. 2200 2201 addBackupTrace("data delivered: " + mStatus); 2202 if (mStatus == BackupConstants.TRANSPORT_OK) { 2203 addBackupTrace("finishing op on transport"); 2204 mStatus = mTransport.finishBackup(); 2205 addBackupTrace("finished: " + mStatus); 2206 } 2207 } else { 2208 if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport"); 2209 addBackupTrace("no data to send"); 2210 } 2211 2212 // After successful transport, delete the now-stale data 2213 // and juggle the files so that next time we supply the agent 2214 // with the new state file it just created. 2215 if (mStatus == BackupConstants.TRANSPORT_OK) { 2216 mBackupDataName.delete(); 2217 mNewStateName.renameTo(mSavedStateName); 2218 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, 2219 mCurrentPackage.packageName, size); 2220 logBackupComplete(mCurrentPackage.packageName); 2221 } else { 2222 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, 2223 mCurrentPackage.packageName); 2224 } 2225 } catch (Exception e) { 2226 Slog.e(TAG, "Transport error backing up " + mCurrentPackage.packageName, e); 2227 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, 2228 mCurrentPackage.packageName); 2229 mStatus = BackupConstants.TRANSPORT_ERROR; 2230 } finally { 2231 try { if (backupData != null) backupData.close(); } catch (IOException e) {} 2232 } 2233 2234 // If we encountered an error here it's a transport-level failure. That 2235 // means we need to halt everything and reschedule everything for next time. 2236 final BackupState nextState; 2237 if (mStatus != BackupConstants.TRANSPORT_OK) { 2238 revertAndEndBackup(); 2239 nextState = BackupState.FINAL; 2240 } else { 2241 // Success! Proceed with the next app if any, otherwise we're done. 2242 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE; 2243 } 2244 2245 executeNextState(nextState); 2246 } 2247 2248 @Override 2249 public void handleTimeout() { 2250 // Whoops, the current agent timed out running doBackup(). Tidy up and restage 2251 // it for the next time we run a backup pass. 2252 // !!! TODO: keep track of failure counts per agent, and blacklist those which 2253 // fail repeatedly (i.e. have proved themselves to be buggy). 2254 Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName); 2255 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName, 2256 "timeout"); 2257 addBackupTrace("timeout of " + mCurrentPackage.packageName); 2258 agentErrorCleanup(); 2259 dataChangedImpl(mCurrentPackage.packageName); 2260 } 2261 2262 void revertAndEndBackup() { 2263 if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything"); 2264 addBackupTrace("transport error; reverting"); 2265 for (BackupRequest request : mOriginalQueue) { 2266 dataChangedImpl(request.packageName); 2267 } 2268 // We also want to reset the backup schedule based on whatever 2269 // the transport suggests by way of retry/backoff time. 2270 restartBackupAlarm(); 2271 } 2272 2273 void agentErrorCleanup() { 2274 mBackupDataName.delete(); 2275 mNewStateName.delete(); 2276 clearAgentState(); 2277 2278 executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE); 2279 } 2280 2281 // Cleanup common to both success and failure cases 2282 void clearAgentState() { 2283 try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {} 2284 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 2285 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 2286 mSavedState = mBackupData = mNewState = null; 2287 synchronized (mCurrentOpLock) { 2288 mCurrentOperations.clear(); 2289 } 2290 2291 // If this was a pseudopackage there's no associated Activity Manager state 2292 if (mCurrentPackage.applicationInfo != null) { 2293 addBackupTrace("unbinding " + mCurrentPackage.packageName); 2294 try { // unbind even on timeout, just in case 2295 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 2296 } catch (RemoteException e) {} 2297 } 2298 } 2299 2300 void restartBackupAlarm() { 2301 addBackupTrace("setting backup trigger"); 2302 synchronized (mQueueLock) { 2303 try { 2304 startBackupAlarmsLocked(mTransport.requestBackupTime()); 2305 } catch (RemoteException e) { /* cannot happen */ } 2306 } 2307 } 2308 2309 void executeNextState(BackupState nextState) { 2310 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 2311 + this + " nextState=" + nextState); 2312 addBackupTrace("executeNextState => " + nextState); 2313 mCurrentState = nextState; 2314 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 2315 mBackupHandler.sendMessage(msg); 2316 } 2317 } 2318 2319 2320 // ----- Full backup/restore to a file/socket ----- 2321 2322 abstract class ObbServiceClient { 2323 public IObbBackupService mObbService; 2324 public void setObbBinder(IObbBackupService binder) { 2325 mObbService = binder; 2326 } 2327 } 2328 2329 class FullBackupObbConnection implements ServiceConnection { 2330 volatile IObbBackupService mService; 2331 2332 FullBackupObbConnection() { 2333 mService = null; 2334 } 2335 2336 public void establish() { 2337 if (DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this); 2338 Intent obbIntent = new Intent().setComponent(new ComponentName( 2339 "com.android.sharedstoragebackup", 2340 "com.android.sharedstoragebackup.ObbBackupService")); 2341 BackupManagerService.this.mContext.bindService( 2342 obbIntent, this, Context.BIND_AUTO_CREATE); 2343 } 2344 2345 public void tearDown() { 2346 BackupManagerService.this.mContext.unbindService(this); 2347 } 2348 2349 public boolean backupObbs(PackageInfo pkg, OutputStream out) { 2350 boolean success = false; 2351 waitForConnection(); 2352 2353 ParcelFileDescriptor[] pipes = null; 2354 try { 2355 pipes = ParcelFileDescriptor.createPipe(); 2356 int token = generateToken(); 2357 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 2358 mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder); 2359 routeSocketDataToOutput(pipes[0], out); 2360 success = waitUntilOperationComplete(token); 2361 } catch (Exception e) { 2362 Slog.w(TAG, "Unable to back up OBBs for " + pkg, e); 2363 } finally { 2364 try { 2365 out.flush(); 2366 if (pipes != null) { 2367 if (pipes[0] != null) pipes[0].close(); 2368 if (pipes[1] != null) pipes[1].close(); 2369 } 2370 } catch (IOException e) { 2371 Slog.w(TAG, "I/O error closing down OBB backup", e); 2372 } 2373 } 2374 return success; 2375 } 2376 2377 public void restoreObbFile(String pkgName, ParcelFileDescriptor data, 2378 long fileSize, int type, String path, long mode, long mtime, 2379 int token, IBackupManager callbackBinder) { 2380 waitForConnection(); 2381 2382 try { 2383 mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime, 2384 token, callbackBinder); 2385 } catch (Exception e) { 2386 Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e); 2387 } 2388 } 2389 2390 private void waitForConnection() { 2391 synchronized (this) { 2392 while (mService == null) { 2393 if (DEBUG) Slog.i(TAG, "...waiting for OBB service binding..."); 2394 try { 2395 this.wait(); 2396 } catch (InterruptedException e) { /* never interrupted */ } 2397 } 2398 if (DEBUG) Slog.i(TAG, "Connected to OBB service; continuing"); 2399 } 2400 } 2401 2402 @Override 2403 public void onServiceConnected(ComponentName name, IBinder service) { 2404 synchronized (this) { 2405 mService = IObbBackupService.Stub.asInterface(service); 2406 if (DEBUG) Slog.i(TAG, "OBB service connection " + mService 2407 + " connected on " + this); 2408 this.notifyAll(); 2409 } 2410 } 2411 2412 @Override 2413 public void onServiceDisconnected(ComponentName name) { 2414 synchronized (this) { 2415 mService = null; 2416 if (DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this); 2417 this.notifyAll(); 2418 } 2419 } 2420 2421 } 2422 2423 private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out) 2424 throws IOException { 2425 FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor()); 2426 DataInputStream in = new DataInputStream(raw); 2427 2428 byte[] buffer = new byte[32 * 1024]; 2429 int chunkTotal; 2430 while ((chunkTotal = in.readInt()) > 0) { 2431 while (chunkTotal > 0) { 2432 int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal; 2433 int nRead = in.read(buffer, 0, toRead); 2434 out.write(buffer, 0, nRead); 2435 chunkTotal -= nRead; 2436 } 2437 } 2438 } 2439 2440 class PerformFullBackupTask extends ObbServiceClient implements Runnable { 2441 ParcelFileDescriptor mOutputFile; 2442 DeflaterOutputStream mDeflater; 2443 IFullBackupRestoreObserver mObserver; 2444 boolean mIncludeApks; 2445 boolean mIncludeObbs; 2446 boolean mIncludeShared; 2447 boolean mAllApps; 2448 final boolean mIncludeSystem; 2449 String[] mPackages; 2450 String mCurrentPassword; 2451 String mEncryptPassword; 2452 AtomicBoolean mLatchObject; 2453 File mFilesDir; 2454 File mManifestFile; 2455 2456 2457 class FullBackupRunner implements Runnable { 2458 PackageInfo mPackage; 2459 IBackupAgent mAgent; 2460 ParcelFileDescriptor mPipe; 2461 int mToken; 2462 boolean mSendApk; 2463 boolean mWriteManifest; 2464 2465 FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe, 2466 int token, boolean sendApk, boolean writeManifest) throws IOException { 2467 mPackage = pack; 2468 mAgent = agent; 2469 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor()); 2470 mToken = token; 2471 mSendApk = sendApk; 2472 mWriteManifest = writeManifest; 2473 } 2474 2475 @Override 2476 public void run() { 2477 try { 2478 BackupDataOutput output = new BackupDataOutput( 2479 mPipe.getFileDescriptor()); 2480 2481 if (mWriteManifest) { 2482 if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName); 2483 writeAppManifest(mPackage, mManifestFile, mSendApk); 2484 FullBackup.backupToTar(mPackage.packageName, null, null, 2485 mFilesDir.getAbsolutePath(), 2486 mManifestFile.getAbsolutePath(), 2487 output); 2488 } 2489 2490 if (mSendApk) { 2491 writeApkToBackup(mPackage, output); 2492 } 2493 2494 if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName); 2495 prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null); 2496 mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder); 2497 } catch (IOException e) { 2498 Slog.e(TAG, "Error running full backup for " + mPackage.packageName); 2499 } catch (RemoteException e) { 2500 Slog.e(TAG, "Remote agent vanished during full backup of " 2501 + mPackage.packageName); 2502 } finally { 2503 try { 2504 mPipe.close(); 2505 } catch (IOException e) {} 2506 } 2507 } 2508 } 2509 2510 PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, 2511 boolean includeApks, boolean includeObbs, boolean includeShared, 2512 String curPassword, String encryptPassword, boolean doAllApps, 2513 boolean doSystem, String[] packages, AtomicBoolean latch) { 2514 mOutputFile = fd; 2515 mObserver = observer; 2516 mIncludeApks = includeApks; 2517 mIncludeObbs = includeObbs; 2518 mIncludeShared = includeShared; 2519 mAllApps = doAllApps; 2520 mIncludeSystem = doSystem; 2521 mPackages = packages; 2522 mCurrentPassword = curPassword; 2523 // when backing up, if there is a current backup password, we require that 2524 // the user use a nonempty encryption password as well. if one is supplied 2525 // in the UI we use that, but if the UI was left empty we fall back to the 2526 // current backup password (which was supplied by the user as well). 2527 if (encryptPassword == null || "".equals(encryptPassword)) { 2528 mEncryptPassword = curPassword; 2529 } else { 2530 mEncryptPassword = encryptPassword; 2531 } 2532 mLatchObject = latch; 2533 2534 mFilesDir = new File("/data/system"); 2535 mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME); 2536 } 2537 2538 @Override 2539 public void run() { 2540 Slog.i(TAG, "--- Performing full-dataset backup ---"); 2541 2542 List<PackageInfo> packagesToBackup = new ArrayList<PackageInfo>(); 2543 FullBackupObbConnection obbConnection = new FullBackupObbConnection(); 2544 obbConnection.establish(); // we'll want this later 2545 2546 sendStartBackup(); 2547 2548 // doAllApps supersedes the package set if any 2549 if (mAllApps) { 2550 packagesToBackup = mPackageManager.getInstalledPackages( 2551 PackageManager.GET_SIGNATURES); 2552 // Exclude system apps if we've been asked to do so 2553 if (mIncludeSystem == false) { 2554 for (int i = 0; i < packagesToBackup.size(); ) { 2555 PackageInfo pkg = packagesToBackup.get(i); 2556 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 2557 packagesToBackup.remove(i); 2558 } else { 2559 i++; 2560 } 2561 } 2562 } 2563 } 2564 2565 // Now process the command line argument packages, if any. Note that explicitly- 2566 // named system-partition packages will be included even if includeSystem was 2567 // set to false. 2568 if (mPackages != null) { 2569 for (String pkgName : mPackages) { 2570 try { 2571 packagesToBackup.add(mPackageManager.getPackageInfo(pkgName, 2572 PackageManager.GET_SIGNATURES)); 2573 } catch (NameNotFoundException e) { 2574 Slog.w(TAG, "Unknown package " + pkgName + ", skipping"); 2575 } 2576 } 2577 } 2578 2579 // Cull any packages that have indicated that backups are not permitted, as well 2580 // as any explicit mention of the 'special' shared-storage agent package (we 2581 // handle that one at the end). 2582 for (int i = 0; i < packagesToBackup.size(); ) { 2583 PackageInfo pkg = packagesToBackup.get(i); 2584 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0 2585 || pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) { 2586 packagesToBackup.remove(i); 2587 } else { 2588 i++; 2589 } 2590 } 2591 2592 // Cull any packages that run as system-domain uids but do not define their 2593 // own backup agents 2594 for (int i = 0; i < packagesToBackup.size(); ) { 2595 PackageInfo pkg = packagesToBackup.get(i); 2596 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 2597 && (pkg.applicationInfo.backupAgentName == null)) { 2598 if (MORE_DEBUG) { 2599 Slog.i(TAG, "... ignoring non-agent system package " + pkg.packageName); 2600 } 2601 packagesToBackup.remove(i); 2602 } else { 2603 i++; 2604 } 2605 } 2606 2607 FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor()); 2608 OutputStream out = null; 2609 2610 PackageInfo pkg = null; 2611 try { 2612 boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0); 2613 boolean compressing = COMPRESS_FULL_BACKUPS; 2614 OutputStream finalOutput = ofstream; 2615 2616 // Verify that the given password matches the currently-active 2617 // backup password, if any 2618 if (hasBackupPassword()) { 2619 if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) { 2620 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 2621 return; 2622 } 2623 } 2624 2625 // Write the global file header. All strings are UTF-8 encoded; lines end 2626 // with a '\n' byte. Actual backup data begins immediately following the 2627 // final '\n'. 2628 // 2629 // line 1: "ANDROID BACKUP" 2630 // line 2: backup file format version, currently "1" 2631 // line 3: compressed? "0" if not compressed, "1" if compressed. 2632 // line 4: name of encryption algorithm [currently only "none" or "AES-256"] 2633 // 2634 // When line 4 is not "none", then additional header data follows: 2635 // 2636 // line 5: user password salt [hex] 2637 // line 6: master key checksum salt [hex] 2638 // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal] 2639 // line 8: IV of the user key [hex] 2640 // line 9: master key blob [hex] 2641 // IV of the master key, master key itself, master key checksum hash 2642 // 2643 // The master key checksum is the master key plus its checksum salt, run through 2644 // 10k rounds of PBKDF2. This is used to verify that the user has supplied the 2645 // correct password for decrypting the archive: the master key decrypted from 2646 // the archive using the user-supplied password is also run through PBKDF2 in 2647 // this way, and if the result does not match the checksum as stored in the 2648 // archive, then we know that the user-supplied password does not match the 2649 // archive's. 2650 StringBuilder headerbuf = new StringBuilder(1024); 2651 2652 headerbuf.append(BACKUP_FILE_HEADER_MAGIC); 2653 headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n 2654 headerbuf.append(compressing ? "\n1\n" : "\n0\n"); 2655 2656 try { 2657 // Set up the encryption stage if appropriate, and emit the correct header 2658 if (encrypting) { 2659 finalOutput = emitAesBackupHeader(headerbuf, finalOutput); 2660 } else { 2661 headerbuf.append("none\n"); 2662 } 2663 2664 byte[] header = headerbuf.toString().getBytes("UTF-8"); 2665 ofstream.write(header); 2666 2667 // Set up the compression stage feeding into the encryption stage (if any) 2668 if (compressing) { 2669 Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION); 2670 finalOutput = new DeflaterOutputStream(finalOutput, deflater, true); 2671 } 2672 2673 out = finalOutput; 2674 } catch (Exception e) { 2675 // Should never happen! 2676 Slog.e(TAG, "Unable to emit archive header", e); 2677 return; 2678 } 2679 2680 // Shared storage if requested 2681 if (mIncludeShared) { 2682 try { 2683 pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0); 2684 packagesToBackup.add(pkg); 2685 } catch (NameNotFoundException e) { 2686 Slog.e(TAG, "Unable to find shared-storage backup handler"); 2687 } 2688 } 2689 2690 // Now back up the app data via the agent mechanism 2691 int N = packagesToBackup.size(); 2692 for (int i = 0; i < N; i++) { 2693 pkg = packagesToBackup.get(i); 2694 backupOnePackage(pkg, out); 2695 2696 // after the app's agent runs to handle its private filesystem 2697 // contents, back up any OBB content it has on its behalf. 2698 if (mIncludeObbs) { 2699 boolean obbOkay = obbConnection.backupObbs(pkg, out); 2700 if (!obbOkay) { 2701 throw new RuntimeException("Failure writing OBB stack for " + pkg); 2702 } 2703 } 2704 } 2705 2706 // Done! 2707 finalizeBackup(out); 2708 } catch (RemoteException e) { 2709 Slog.e(TAG, "App died during full backup"); 2710 } catch (Exception e) { 2711 Slog.e(TAG, "Internal exception during full backup", e); 2712 } finally { 2713 tearDown(pkg); 2714 try { 2715 if (out != null) out.close(); 2716 mOutputFile.close(); 2717 } catch (IOException e) { 2718 /* nothing we can do about this */ 2719 } 2720 synchronized (mCurrentOpLock) { 2721 mCurrentOperations.clear(); 2722 } 2723 synchronized (mLatchObject) { 2724 mLatchObject.set(true); 2725 mLatchObject.notifyAll(); 2726 } 2727 sendEndBackup(); 2728 obbConnection.tearDown(); 2729 if (DEBUG) Slog.d(TAG, "Full backup pass complete."); 2730 mWakelock.release(); 2731 } 2732 } 2733 2734 private OutputStream emitAesBackupHeader(StringBuilder headerbuf, 2735 OutputStream ofstream) throws Exception { 2736 // User key will be used to encrypt the master key. 2737 byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE); 2738 SecretKey userKey = buildPasswordKey(mEncryptPassword, newUserSalt, 2739 PBKDF2_HASH_ROUNDS); 2740 2741 // the master key is random for each backup 2742 byte[] masterPw = new byte[256 / 8]; 2743 mRng.nextBytes(masterPw); 2744 byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE); 2745 2746 // primary encryption of the datastream with the random key 2747 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 2748 SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES"); 2749 c.init(Cipher.ENCRYPT_MODE, masterKeySpec); 2750 OutputStream finalOutput = new CipherOutputStream(ofstream, c); 2751 2752 // line 4: name of encryption algorithm 2753 headerbuf.append(ENCRYPTION_ALGORITHM_NAME); 2754 headerbuf.append('\n'); 2755 // line 5: user password salt [hex] 2756 headerbuf.append(byteArrayToHex(newUserSalt)); 2757 headerbuf.append('\n'); 2758 // line 6: master key checksum salt [hex] 2759 headerbuf.append(byteArrayToHex(checksumSalt)); 2760 headerbuf.append('\n'); 2761 // line 7: number of PBKDF2 rounds used [decimal] 2762 headerbuf.append(PBKDF2_HASH_ROUNDS); 2763 headerbuf.append('\n'); 2764 2765 // line 8: IV of the user key [hex] 2766 Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding"); 2767 mkC.init(Cipher.ENCRYPT_MODE, userKey); 2768 2769 byte[] IV = mkC.getIV(); 2770 headerbuf.append(byteArrayToHex(IV)); 2771 headerbuf.append('\n'); 2772 2773 // line 9: master IV + key blob, encrypted by the user key [hex]. Blob format: 2774 // [byte] IV length = Niv 2775 // [array of Niv bytes] IV itself 2776 // [byte] master key length = Nmk 2777 // [array of Nmk bytes] master key itself 2778 // [byte] MK checksum hash length = Nck 2779 // [array of Nck bytes] master key checksum hash 2780 // 2781 // The checksum is the (master key + checksum salt), run through the 2782 // stated number of PBKDF2 rounds 2783 IV = c.getIV(); 2784 byte[] mk = masterKeySpec.getEncoded(); 2785 byte[] checksum = makeKeyChecksum(masterKeySpec.getEncoded(), 2786 checksumSalt, PBKDF2_HASH_ROUNDS); 2787 2788 ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length 2789 + checksum.length + 3); 2790 DataOutputStream mkOut = new DataOutputStream(blob); 2791 mkOut.writeByte(IV.length); 2792 mkOut.write(IV); 2793 mkOut.writeByte(mk.length); 2794 mkOut.write(mk); 2795 mkOut.writeByte(checksum.length); 2796 mkOut.write(checksum); 2797 mkOut.flush(); 2798 byte[] encryptedMk = mkC.doFinal(blob.toByteArray()); 2799 headerbuf.append(byteArrayToHex(encryptedMk)); 2800 headerbuf.append('\n'); 2801 2802 return finalOutput; 2803 } 2804 2805 private void backupOnePackage(PackageInfo pkg, OutputStream out) 2806 throws RemoteException { 2807 Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName); 2808 2809 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo, 2810 IApplicationThread.BACKUP_MODE_FULL); 2811 if (agent != null) { 2812 ParcelFileDescriptor[] pipes = null; 2813 try { 2814 pipes = ParcelFileDescriptor.createPipe(); 2815 2816 ApplicationInfo app = pkg.applicationInfo; 2817 final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE); 2818 final boolean sendApk = mIncludeApks 2819 && !isSharedStorage 2820 && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0) 2821 && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 || 2822 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); 2823 2824 sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName); 2825 2826 final int token = generateToken(); 2827 FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1], 2828 token, sendApk, !isSharedStorage); 2829 pipes[1].close(); // the runner has dup'd it 2830 pipes[1] = null; 2831 Thread t = new Thread(runner); 2832 t.start(); 2833 2834 // Now pull data from the app and stuff it into the compressor 2835 try { 2836 routeSocketDataToOutput(pipes[0], out); 2837 } catch (IOException e) { 2838 Slog.i(TAG, "Caught exception reading from agent", e); 2839 } 2840 2841 if (!waitUntilOperationComplete(token)) { 2842 Slog.e(TAG, "Full backup failed on package " + pkg.packageName); 2843 } else { 2844 if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName); 2845 } 2846 2847 } catch (IOException e) { 2848 Slog.e(TAG, "Error backing up " + pkg.packageName, e); 2849 } finally { 2850 try { 2851 // flush after every package 2852 out.flush(); 2853 if (pipes != null) { 2854 if (pipes[0] != null) pipes[0].close(); 2855 if (pipes[1] != null) pipes[1].close(); 2856 } 2857 } catch (IOException e) { 2858 Slog.w(TAG, "Error bringing down backup stack"); 2859 } 2860 } 2861 } else { 2862 Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName); 2863 } 2864 tearDown(pkg); 2865 } 2866 2867 private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) { 2868 // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here 2869 final String appSourceDir = pkg.applicationInfo.sourceDir; 2870 final String apkDir = new File(appSourceDir).getParent(); 2871 FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null, 2872 apkDir, appSourceDir, output); 2873 2874 // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM 2875 // doesn't have access to external storage. 2876 2877 // Save associated .obb content if it exists and we did save the apk 2878 // check for .obb and save those too 2879 final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_OWNER); 2880 final File obbDir = userEnv.getExternalStorageAppObbDirectory(pkg.packageName); 2881 if (obbDir != null) { 2882 if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); 2883 File[] obbFiles = obbDir.listFiles(); 2884 if (obbFiles != null) { 2885 final String obbDirName = obbDir.getAbsolutePath(); 2886 for (File obb : obbFiles) { 2887 FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null, 2888 obbDirName, obb.getAbsolutePath(), output); 2889 } 2890 } 2891 } 2892 } 2893 2894 private void finalizeBackup(OutputStream out) { 2895 try { 2896 // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes. 2897 byte[] eof = new byte[512 * 2]; // newly allocated == zero filled 2898 out.write(eof); 2899 } catch (IOException e) { 2900 Slog.w(TAG, "Error attempting to finalize backup stream"); 2901 } 2902 } 2903 2904 private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk) 2905 throws IOException { 2906 // Manifest format. All data are strings ending in LF: 2907 // BACKUP_MANIFEST_VERSION, currently 1 2908 // 2909 // Version 1: 2910 // package name 2911 // package's versionCode 2912 // platform versionCode 2913 // getInstallerPackageName() for this package (maybe empty) 2914 // boolean: "1" if archive includes .apk; any other string means not 2915 // number of signatures == N 2916 // N*: signature byte array in ascii format per Signature.toCharsString() 2917 StringBuilder builder = new StringBuilder(4096); 2918 StringBuilderPrinter printer = new StringBuilderPrinter(builder); 2919 2920 printer.println(Integer.toString(BACKUP_MANIFEST_VERSION)); 2921 printer.println(pkg.packageName); 2922 printer.println(Integer.toString(pkg.versionCode)); 2923 printer.println(Integer.toString(Build.VERSION.SDK_INT)); 2924 2925 String installerName = mPackageManager.getInstallerPackageName(pkg.packageName); 2926 printer.println((installerName != null) ? installerName : ""); 2927 2928 printer.println(withApk ? "1" : "0"); 2929 if (pkg.signatures == null) { 2930 printer.println("0"); 2931 } else { 2932 printer.println(Integer.toString(pkg.signatures.length)); 2933 for (Signature sig : pkg.signatures) { 2934 printer.println(sig.toCharsString()); 2935 } 2936 } 2937 2938 FileOutputStream outstream = new FileOutputStream(manifestFile); 2939 outstream.write(builder.toString().getBytes()); 2940 outstream.close(); 2941 } 2942 2943 private void tearDown(PackageInfo pkg) { 2944 if (pkg != null) { 2945 final ApplicationInfo app = pkg.applicationInfo; 2946 if (app != null) { 2947 try { 2948 // unbind and tidy up even on timeout or failure, just in case 2949 mActivityManager.unbindBackupAgent(app); 2950 2951 // The agent was running with a stub Application object, so shut it down. 2952 if (app.uid != Process.SYSTEM_UID 2953 && app.uid != Process.PHONE_UID) { 2954 if (MORE_DEBUG) Slog.d(TAG, "Backup complete, killing host process"); 2955 mActivityManager.killApplicationProcess(app.processName, app.uid); 2956 } else { 2957 if (MORE_DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName); 2958 } 2959 } catch (RemoteException e) { 2960 Slog.d(TAG, "Lost app trying to shut down"); 2961 } 2962 } 2963 } 2964 } 2965 2966 // wrappers for observer use 2967 void sendStartBackup() { 2968 if (mObserver != null) { 2969 try { 2970 mObserver.onStartBackup(); 2971 } catch (RemoteException e) { 2972 Slog.w(TAG, "full backup observer went away: startBackup"); 2973 mObserver = null; 2974 } 2975 } 2976 } 2977 2978 void sendOnBackupPackage(String name) { 2979 if (mObserver != null) { 2980 try { 2981 // TODO: use a more user-friendly name string 2982 mObserver.onBackupPackage(name); 2983 } catch (RemoteException e) { 2984 Slog.w(TAG, "full backup observer went away: backupPackage"); 2985 mObserver = null; 2986 } 2987 } 2988 } 2989 2990 void sendEndBackup() { 2991 if (mObserver != null) { 2992 try { 2993 mObserver.onEndBackup(); 2994 } catch (RemoteException e) { 2995 Slog.w(TAG, "full backup observer went away: endBackup"); 2996 mObserver = null; 2997 } 2998 } 2999 } 3000 } 3001 3002 3003 // ----- Full restore from a file/socket ----- 3004 3005 // Description of a file in the restore datastream 3006 static class FileMetadata { 3007 String packageName; // name of the owning app 3008 String installerPackageName; // name of the market-type app that installed the owner 3009 int type; // e.g. BackupAgent.TYPE_DIRECTORY 3010 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN 3011 String path; // subpath within the semantic domain 3012 long mode; // e.g. 0666 (actually int) 3013 long mtime; // last mod time, UTC time_t (actually int) 3014 long size; // bytes of content 3015 3016 @Override 3017 public String toString() { 3018 StringBuilder sb = new StringBuilder(128); 3019 sb.append("FileMetadata{"); 3020 sb.append(packageName); sb.append(','); 3021 sb.append(type); sb.append(','); 3022 sb.append(domain); sb.append(':'); sb.append(path); sb.append(','); 3023 sb.append(size); 3024 sb.append('}'); 3025 return sb.toString(); 3026 } 3027 } 3028 3029 enum RestorePolicy { 3030 IGNORE, 3031 ACCEPT, 3032 ACCEPT_IF_APK 3033 } 3034 3035 class PerformFullRestoreTask extends ObbServiceClient implements Runnable { 3036 ParcelFileDescriptor mInputFile; 3037 String mCurrentPassword; 3038 String mDecryptPassword; 3039 IFullBackupRestoreObserver mObserver; 3040 AtomicBoolean mLatchObject; 3041 IBackupAgent mAgent; 3042 String mAgentPackage; 3043 ApplicationInfo mTargetApp; 3044 FullBackupObbConnection mObbConnection = null; 3045 ParcelFileDescriptor[] mPipes = null; 3046 3047 long mBytes; 3048 3049 // possible handling states for a given package in the restore dataset 3050 final HashMap<String, RestorePolicy> mPackagePolicies 3051 = new HashMap<String, RestorePolicy>(); 3052 3053 // installer package names for each encountered app, derived from the manifests 3054 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 3055 3056 // Signatures for a given package found in its manifest file 3057 final HashMap<String, Signature[]> mManifestSignatures 3058 = new HashMap<String, Signature[]>(); 3059 3060 // Packages we've already wiped data on when restoring their first file 3061 final HashSet<String> mClearedPackages = new HashSet<String>(); 3062 3063 PerformFullRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword, 3064 IFullBackupRestoreObserver observer, AtomicBoolean latch) { 3065 mInputFile = fd; 3066 mCurrentPassword = curPassword; 3067 mDecryptPassword = decryptPassword; 3068 mObserver = observer; 3069 mLatchObject = latch; 3070 mAgent = null; 3071 mAgentPackage = null; 3072 mTargetApp = null; 3073 mObbConnection = new FullBackupObbConnection(); 3074 3075 // Which packages we've already wiped data on. We prepopulate this 3076 // with a whitelist of packages known to be unclearable. 3077 mClearedPackages.add("android"); 3078 mClearedPackages.add("com.android.providers.settings"); 3079 3080 } 3081 3082 class RestoreFileRunnable implements Runnable { 3083 IBackupAgent mAgent; 3084 FileMetadata mInfo; 3085 ParcelFileDescriptor mSocket; 3086 int mToken; 3087 3088 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 3089 ParcelFileDescriptor socket, int token) throws IOException { 3090 mAgent = agent; 3091 mInfo = info; 3092 mToken = token; 3093 3094 // This class is used strictly for process-local binder invocations. The 3095 // semantics of ParcelFileDescriptor differ in this case; in particular, we 3096 // do not automatically get a 'dup'ed descriptor that we can can continue 3097 // to use asynchronously from the caller. So, we make sure to dup it ourselves 3098 // before proceeding to do the restore. 3099 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 3100 } 3101 3102 @Override 3103 public void run() { 3104 try { 3105 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 3106 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 3107 mToken, mBackupManagerBinder); 3108 } catch (RemoteException e) { 3109 // never happens; this is used strictly for local binder calls 3110 } 3111 } 3112 } 3113 3114 @Override 3115 public void run() { 3116 Slog.i(TAG, "--- Performing full-dataset restore ---"); 3117 mObbConnection.establish(); 3118 sendStartRestore(); 3119 3120 // Are we able to restore shared-storage data? 3121 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 3122 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT); 3123 } 3124 3125 FileInputStream rawInStream = null; 3126 DataInputStream rawDataIn = null; 3127 try { 3128 if (hasBackupPassword()) { 3129 if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) { 3130 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 3131 return; 3132 } 3133 } 3134 3135 mBytes = 0; 3136 byte[] buffer = new byte[32 * 1024]; 3137 rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); 3138 rawDataIn = new DataInputStream(rawInStream); 3139 3140 // First, parse out the unencrypted/uncompressed header 3141 boolean compressed = false; 3142 InputStream preCompressStream = rawInStream; 3143 final InputStream in; 3144 3145 boolean okay = false; 3146 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length(); 3147 byte[] streamHeader = new byte[headerLen]; 3148 rawDataIn.readFully(streamHeader); 3149 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8"); 3150 if (Arrays.equals(magicBytes, streamHeader)) { 3151 // okay, header looks good. now parse out the rest of the fields. 3152 String s = readHeaderLine(rawInStream); 3153 if (Integer.parseInt(s) == BACKUP_FILE_VERSION) { 3154 // okay, it's a version we recognize 3155 s = readHeaderLine(rawInStream); 3156 compressed = (Integer.parseInt(s) != 0); 3157 s = readHeaderLine(rawInStream); 3158 if (s.equals("none")) { 3159 // no more header to parse; we're good to go 3160 okay = true; 3161 } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) { 3162 preCompressStream = decodeAesHeaderAndInitialize(s, rawInStream); 3163 if (preCompressStream != null) { 3164 okay = true; 3165 } 3166 } else Slog.w(TAG, "Archive is encrypted but no password given"); 3167 } else Slog.w(TAG, "Wrong header version: " + s); 3168 } else Slog.w(TAG, "Didn't read the right header magic"); 3169 3170 if (!okay) { 3171 Slog.w(TAG, "Invalid restore data; aborting."); 3172 return; 3173 } 3174 3175 // okay, use the right stream layer based on compression 3176 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream; 3177 3178 boolean didRestore; 3179 do { 3180 didRestore = restoreOneFile(in, buffer); 3181 } while (didRestore); 3182 3183 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); 3184 } catch (IOException e) { 3185 Slog.e(TAG, "Unable to read restore input"); 3186 } finally { 3187 tearDownPipes(); 3188 tearDownAgent(mTargetApp); 3189 3190 try { 3191 if (rawDataIn != null) rawDataIn.close(); 3192 if (rawInStream != null) rawInStream.close(); 3193 mInputFile.close(); 3194 } catch (IOException e) { 3195 Slog.w(TAG, "Close of restore data pipe threw", e); 3196 /* nothing we can do about this */ 3197 } 3198 synchronized (mCurrentOpLock) { 3199 mCurrentOperations.clear(); 3200 } 3201 synchronized (mLatchObject) { 3202 mLatchObject.set(true); 3203 mLatchObject.notifyAll(); 3204 } 3205 mObbConnection.tearDown(); 3206 sendEndRestore(); 3207 Slog.d(TAG, "Full restore pass complete."); 3208 mWakelock.release(); 3209 } 3210 } 3211 3212 String readHeaderLine(InputStream in) throws IOException { 3213 int c; 3214 StringBuilder buffer = new StringBuilder(80); 3215 while ((c = in.read()) >= 0) { 3216 if (c == '\n') break; // consume and discard the newlines 3217 buffer.append((char)c); 3218 } 3219 return buffer.toString(); 3220 } 3221 3222 InputStream decodeAesHeaderAndInitialize(String encryptionName, InputStream rawInStream) { 3223 InputStream result = null; 3224 try { 3225 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) { 3226 3227 String userSaltHex = readHeaderLine(rawInStream); // 5 3228 byte[] userSalt = hexToByteArray(userSaltHex); 3229 3230 String ckSaltHex = readHeaderLine(rawInStream); // 6 3231 byte[] ckSalt = hexToByteArray(ckSaltHex); 3232 3233 int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 3234 String userIvHex = readHeaderLine(rawInStream); // 8 3235 3236 String masterKeyBlobHex = readHeaderLine(rawInStream); // 9 3237 3238 // decrypt the master key blob 3239 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 3240 SecretKey userKey = buildPasswordKey(mDecryptPassword, userSalt, 3241 rounds); 3242 byte[] IV = hexToByteArray(userIvHex); 3243 IvParameterSpec ivSpec = new IvParameterSpec(IV); 3244 c.init(Cipher.DECRYPT_MODE, 3245 new SecretKeySpec(userKey.getEncoded(), "AES"), 3246 ivSpec); 3247 byte[] mkCipher = hexToByteArray(masterKeyBlobHex); 3248 byte[] mkBlob = c.doFinal(mkCipher); 3249 3250 // first, the master key IV 3251 int offset = 0; 3252 int len = mkBlob[offset++]; 3253 IV = Arrays.copyOfRange(mkBlob, offset, offset + len); 3254 offset += len; 3255 // then the master key itself 3256 len = mkBlob[offset++]; 3257 byte[] mk = Arrays.copyOfRange(mkBlob, 3258 offset, offset + len); 3259 offset += len; 3260 // and finally the master key checksum hash 3261 len = mkBlob[offset++]; 3262 byte[] mkChecksum = Arrays.copyOfRange(mkBlob, 3263 offset, offset + len); 3264 3265 // now validate the decrypted master key against the checksum 3266 byte[] calculatedCk = makeKeyChecksum(mk, ckSalt, rounds); 3267 if (Arrays.equals(calculatedCk, mkChecksum)) { 3268 ivSpec = new IvParameterSpec(IV); 3269 c.init(Cipher.DECRYPT_MODE, 3270 new SecretKeySpec(mk, "AES"), 3271 ivSpec); 3272 // Only if all of the above worked properly will 'result' be assigned 3273 result = new CipherInputStream(rawInStream, c); 3274 } else Slog.w(TAG, "Incorrect password"); 3275 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName); 3276 } catch (InvalidAlgorithmParameterException e) { 3277 Slog.e(TAG, "Needed parameter spec unavailable!", e); 3278 } catch (BadPaddingException e) { 3279 // This case frequently occurs when the wrong password is used to decrypt 3280 // the master key. Use the identical "incorrect password" log text as is 3281 // used in the checksum failure log in order to avoid providing additional 3282 // information to an attacker. 3283 Slog.w(TAG, "Incorrect password"); 3284 } catch (IllegalBlockSizeException e) { 3285 Slog.w(TAG, "Invalid block size in master key"); 3286 } catch (NoSuchAlgorithmException e) { 3287 Slog.e(TAG, "Needed decryption algorithm unavailable!"); 3288 } catch (NoSuchPaddingException e) { 3289 Slog.e(TAG, "Needed padding mechanism unavailable!"); 3290 } catch (InvalidKeyException e) { 3291 Slog.w(TAG, "Illegal password; aborting"); 3292 } catch (NumberFormatException e) { 3293 Slog.w(TAG, "Can't parse restore data header"); 3294 } catch (IOException e) { 3295 Slog.w(TAG, "Can't read input header"); 3296 } 3297 3298 return result; 3299 } 3300 3301 boolean restoreOneFile(InputStream instream, byte[] buffer) { 3302 FileMetadata info; 3303 try { 3304 info = readTarHeaders(instream); 3305 if (info != null) { 3306 if (MORE_DEBUG) { 3307 dumpFileMetadata(info); 3308 } 3309 3310 final String pkg = info.packageName; 3311 if (!pkg.equals(mAgentPackage)) { 3312 // okay, change in package; set up our various 3313 // bookkeeping if we haven't seen it yet 3314 if (!mPackagePolicies.containsKey(pkg)) { 3315 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 3316 } 3317 3318 // Clean up the previous agent relationship if necessary, 3319 // and let the observer know we're considering a new app. 3320 if (mAgent != null) { 3321 if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one"); 3322 tearDownPipes(); 3323 tearDownAgent(mTargetApp); 3324 mTargetApp = null; 3325 mAgentPackage = null; 3326 } 3327 } 3328 3329 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 3330 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 3331 mPackageInstallers.put(pkg, info.installerPackageName); 3332 // We've read only the manifest content itself at this point, 3333 // so consume the footer before looping around to the next 3334 // input file 3335 skipTarPadding(info.size, instream); 3336 sendOnRestorePackage(pkg); 3337 } else { 3338 // Non-manifest, so it's actual file data. Is this a package 3339 // we're ignoring? 3340 boolean okay = true; 3341 RestorePolicy policy = mPackagePolicies.get(pkg); 3342 switch (policy) { 3343 case IGNORE: 3344 okay = false; 3345 break; 3346 3347 case ACCEPT_IF_APK: 3348 // If we're in accept-if-apk state, then the first file we 3349 // see MUST be the apk. 3350 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 3351 if (DEBUG) Slog.d(TAG, "APK file; installing"); 3352 // Try to install the app. 3353 String installerName = mPackageInstallers.get(pkg); 3354 okay = installApk(info, installerName, instream); 3355 // good to go; promote to ACCEPT 3356 mPackagePolicies.put(pkg, (okay) 3357 ? RestorePolicy.ACCEPT 3358 : RestorePolicy.IGNORE); 3359 // At this point we've consumed this file entry 3360 // ourselves, so just strip the tar footer and 3361 // go on to the next file in the input stream 3362 skipTarPadding(info.size, instream); 3363 return true; 3364 } else { 3365 // File data before (or without) the apk. We can't 3366 // handle it coherently in this case so ignore it. 3367 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 3368 okay = false; 3369 } 3370 break; 3371 3372 case ACCEPT: 3373 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 3374 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 3375 // we can take the data without the apk, so we 3376 // *want* to do so. skip the apk by declaring this 3377 // one file not-okay without changing the restore 3378 // policy for the package. 3379 okay = false; 3380 } 3381 break; 3382 3383 default: 3384 // Something has gone dreadfully wrong when determining 3385 // the restore policy from the manifest. Ignore the 3386 // rest of this package's data. 3387 Slog.e(TAG, "Invalid policy from manifest"); 3388 okay = false; 3389 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 3390 break; 3391 } 3392 3393 // If the policy is satisfied, go ahead and set up to pipe the 3394 // data to the agent. 3395 if (DEBUG && okay && mAgent != null) { 3396 Slog.i(TAG, "Reusing existing agent instance"); 3397 } 3398 if (okay && mAgent == null) { 3399 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 3400 3401 try { 3402 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 3403 3404 // If we haven't sent any data to this app yet, we probably 3405 // need to clear it first. Check that. 3406 if (!mClearedPackages.contains(pkg)) { 3407 // apps with their own backup agents are 3408 // responsible for coherently managing a full 3409 // restore. 3410 if (mTargetApp.backupAgentName == null) { 3411 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 3412 clearApplicationDataSynchronous(pkg); 3413 } else { 3414 if (DEBUG) Slog.d(TAG, "backup agent (" 3415 + mTargetApp.backupAgentName + ") => no clear"); 3416 } 3417 mClearedPackages.add(pkg); 3418 } else { 3419 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 3420 } 3421 3422 // All set; now set up the IPC and launch the agent 3423 setUpPipes(); 3424 mAgent = bindToAgentSynchronous(mTargetApp, 3425 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 3426 mAgentPackage = pkg; 3427 } catch (IOException e) { 3428 // fall through to error handling 3429 } catch (NameNotFoundException e) { 3430 // fall through to error handling 3431 } 3432 3433 if (mAgent == null) { 3434 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 3435 okay = false; 3436 tearDownPipes(); 3437 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 3438 } 3439 } 3440 3441 // Sanity check: make sure we never give data to the wrong app. This 3442 // should never happen but a little paranoia here won't go amiss. 3443 if (okay && !pkg.equals(mAgentPackage)) { 3444 Slog.e(TAG, "Restoring data for " + pkg 3445 + " but agent is for " + mAgentPackage); 3446 okay = false; 3447 } 3448 3449 // At this point we have an agent ready to handle the full 3450 // restore data as well as a pipe for sending data to 3451 // that agent. Tell the agent to start reading from the 3452 // pipe. 3453 if (okay) { 3454 boolean agentSuccess = true; 3455 long toCopy = info.size; 3456 final int token = generateToken(); 3457 try { 3458 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 3459 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 3460 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 3461 + " : " + info.path); 3462 mObbConnection.restoreObbFile(pkg, mPipes[0], 3463 info.size, info.type, info.path, info.mode, 3464 info.mtime, token, mBackupManagerBinder); 3465 } else { 3466 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 3467 + info.path); 3468 // fire up the app's agent listening on the socket. If 3469 // the agent is running in the system process we can't 3470 // just invoke it asynchronously, so we provide a thread 3471 // for it here. 3472 if (mTargetApp.processName.equals("system")) { 3473 Slog.d(TAG, "system process agent - spinning a thread"); 3474 RestoreFileRunnable runner = new RestoreFileRunnable( 3475 mAgent, info, mPipes[0], token); 3476 new Thread(runner).start(); 3477 } else { 3478 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 3479 info.domain, info.path, info.mode, info.mtime, 3480 token, mBackupManagerBinder); 3481 } 3482 } 3483 } catch (IOException e) { 3484 // couldn't dup the socket for a process-local restore 3485 Slog.d(TAG, "Couldn't establish restore"); 3486 agentSuccess = false; 3487 okay = false; 3488 } catch (RemoteException e) { 3489 // whoops, remote entity went away. We'll eat the content 3490 // ourselves, then, and not copy it over. 3491 Slog.e(TAG, "Agent crashed during full restore"); 3492 agentSuccess = false; 3493 okay = false; 3494 } 3495 3496 // Copy over the data if the agent is still good 3497 if (okay) { 3498 boolean pipeOkay = true; 3499 FileOutputStream pipe = new FileOutputStream( 3500 mPipes[1].getFileDescriptor()); 3501 while (toCopy > 0) { 3502 int toRead = (toCopy > buffer.length) 3503 ? buffer.length : (int)toCopy; 3504 int nRead = instream.read(buffer, 0, toRead); 3505 if (nRead >= 0) mBytes += nRead; 3506 if (nRead <= 0) break; 3507 toCopy -= nRead; 3508 3509 // send it to the output pipe as long as things 3510 // are still good 3511 if (pipeOkay) { 3512 try { 3513 pipe.write(buffer, 0, nRead); 3514 } catch (IOException e) { 3515 Slog.e(TAG, "Failed to write to restore pipe", e); 3516 pipeOkay = false; 3517 } 3518 } 3519 } 3520 3521 // done sending that file! Now we just need to consume 3522 // the delta from info.size to the end of block. 3523 skipTarPadding(info.size, instream); 3524 3525 // and now that we've sent it all, wait for the remote 3526 // side to acknowledge receipt 3527 agentSuccess = waitUntilOperationComplete(token); 3528 } 3529 3530 // okay, if the remote end failed at any point, deal with 3531 // it by ignoring the rest of the restore on it 3532 if (!agentSuccess) { 3533 mBackupHandler.removeMessages(MSG_TIMEOUT); 3534 tearDownPipes(); 3535 tearDownAgent(mTargetApp); 3536 mAgent = null; 3537 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 3538 } 3539 } 3540 3541 // Problems setting up the agent communication, or an already- 3542 // ignored package: skip to the next tar stream entry by 3543 // reading and discarding this file. 3544 if (!okay) { 3545 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 3546 long bytesToConsume = (info.size + 511) & ~511; 3547 while (bytesToConsume > 0) { 3548 int toRead = (bytesToConsume > buffer.length) 3549 ? buffer.length : (int)bytesToConsume; 3550 long nRead = instream.read(buffer, 0, toRead); 3551 if (nRead >= 0) mBytes += nRead; 3552 if (nRead <= 0) break; 3553 bytesToConsume -= nRead; 3554 } 3555 } 3556 } 3557 } 3558 } catch (IOException e) { 3559 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 3560 // treat as EOF 3561 info = null; 3562 } 3563 3564 return (info != null); 3565 } 3566 3567 void setUpPipes() throws IOException { 3568 mPipes = ParcelFileDescriptor.createPipe(); 3569 } 3570 3571 void tearDownPipes() { 3572 if (mPipes != null) { 3573 try { 3574 mPipes[0].close(); 3575 mPipes[0] = null; 3576 mPipes[1].close(); 3577 mPipes[1] = null; 3578 } catch (IOException e) { 3579 Slog.w(TAG, "Couldn't close agent pipes", e); 3580 } 3581 mPipes = null; 3582 } 3583 } 3584 3585 void tearDownAgent(ApplicationInfo app) { 3586 if (mAgent != null) { 3587 try { 3588 // unbind and tidy up even on timeout or failure, just in case 3589 mActivityManager.unbindBackupAgent(app); 3590 3591 // The agent was running with a stub Application object, so shut it down. 3592 // !!! We hardcode the confirmation UI's package name here rather than use a 3593 // manifest flag! TODO something less direct. 3594 if (app.uid != Process.SYSTEM_UID 3595 && !app.packageName.equals("com.android.backupconfirm")) { 3596 if (DEBUG) Slog.d(TAG, "Killing host process"); 3597 mActivityManager.killApplicationProcess(app.processName, app.uid); 3598 } else { 3599 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 3600 } 3601 } catch (RemoteException e) { 3602 Slog.d(TAG, "Lost app trying to shut down"); 3603 } 3604 mAgent = null; 3605 } 3606 } 3607 3608 class RestoreInstallObserver extends IPackageInstallObserver.Stub { 3609 final AtomicBoolean mDone = new AtomicBoolean(); 3610 String mPackageName; 3611 int mResult; 3612 3613 public void reset() { 3614 synchronized (mDone) { 3615 mDone.set(false); 3616 } 3617 } 3618 3619 public void waitForCompletion() { 3620 synchronized (mDone) { 3621 while (mDone.get() == false) { 3622 try { 3623 mDone.wait(); 3624 } catch (InterruptedException e) { } 3625 } 3626 } 3627 } 3628 3629 int getResult() { 3630 return mResult; 3631 } 3632 3633 @Override 3634 public void packageInstalled(String packageName, int returnCode) 3635 throws RemoteException { 3636 synchronized (mDone) { 3637 mResult = returnCode; 3638 mPackageName = packageName; 3639 mDone.set(true); 3640 mDone.notifyAll(); 3641 } 3642 } 3643 } 3644 3645 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 3646 final AtomicBoolean mDone = new AtomicBoolean(); 3647 int mResult; 3648 3649 public void reset() { 3650 synchronized (mDone) { 3651 mDone.set(false); 3652 } 3653 } 3654 3655 public void waitForCompletion() { 3656 synchronized (mDone) { 3657 while (mDone.get() == false) { 3658 try { 3659 mDone.wait(); 3660 } catch (InterruptedException e) { } 3661 } 3662 } 3663 } 3664 3665 @Override 3666 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 3667 synchronized (mDone) { 3668 mResult = returnCode; 3669 mDone.set(true); 3670 mDone.notifyAll(); 3671 } 3672 } 3673 } 3674 3675 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 3676 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 3677 3678 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 3679 boolean okay = true; 3680 3681 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 3682 3683 // The file content is an .apk file. Copy it out to a staging location and 3684 // attempt to install it. 3685 File apkFile = new File(mDataDir, info.packageName); 3686 try { 3687 FileOutputStream apkStream = new FileOutputStream(apkFile); 3688 byte[] buffer = new byte[32 * 1024]; 3689 long size = info.size; 3690 while (size > 0) { 3691 long toRead = (buffer.length < size) ? buffer.length : size; 3692 int didRead = instream.read(buffer, 0, (int)toRead); 3693 if (didRead >= 0) mBytes += didRead; 3694 apkStream.write(buffer, 0, didRead); 3695 size -= didRead; 3696 } 3697 apkStream.close(); 3698 3699 // make sure the installer can read it 3700 apkFile.setReadable(true, false); 3701 3702 // Now install it 3703 Uri packageUri = Uri.fromFile(apkFile); 3704 mInstallObserver.reset(); 3705 mPackageManager.installPackage(packageUri, mInstallObserver, 3706 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 3707 installerPackage); 3708 mInstallObserver.waitForCompletion(); 3709 3710 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 3711 // The only time we continue to accept install of data even if the 3712 // apk install failed is if we had already determined that we could 3713 // accept the data regardless. 3714 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 3715 okay = false; 3716 } 3717 } else { 3718 // Okay, the install succeeded. Make sure it was the right app. 3719 boolean uninstall = false; 3720 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 3721 Slog.w(TAG, "Restore stream claimed to include apk for " 3722 + info.packageName + " but apk was really " 3723 + mInstallObserver.mPackageName); 3724 // delete the package we just put in place; it might be fraudulent 3725 okay = false; 3726 uninstall = true; 3727 } else { 3728 try { 3729 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 3730 PackageManager.GET_SIGNATURES); 3731 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 3732 Slog.w(TAG, "Restore stream contains apk of package " 3733 + info.packageName + " but it disallows backup/restore"); 3734 okay = false; 3735 } else { 3736 // So far so good -- do the signatures match the manifest? 3737 Signature[] sigs = mManifestSignatures.get(info.packageName); 3738 if (signaturesMatch(sigs, pkg)) { 3739 // If this is a system-uid app without a declared backup agent, 3740 // don't restore any of the file data. 3741 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 3742 && (pkg.applicationInfo.backupAgentName == null)) { 3743 Slog.w(TAG, "Installed app " + info.packageName 3744 + " has restricted uid and no agent"); 3745 okay = false; 3746 } 3747 } else { 3748 Slog.w(TAG, "Installed app " + info.packageName 3749 + " signatures do not match restore manifest"); 3750 okay = false; 3751 uninstall = true; 3752 } 3753 } 3754 } catch (NameNotFoundException e) { 3755 Slog.w(TAG, "Install of package " + info.packageName 3756 + " succeeded but now not found"); 3757 okay = false; 3758 } 3759 } 3760 3761 // If we're not okay at this point, we need to delete the package 3762 // that we just installed. 3763 if (uninstall) { 3764 mDeleteObserver.reset(); 3765 mPackageManager.deletePackage(mInstallObserver.mPackageName, 3766 mDeleteObserver, 0); 3767 mDeleteObserver.waitForCompletion(); 3768 } 3769 } 3770 } catch (IOException e) { 3771 Slog.e(TAG, "Unable to transcribe restored apk for install"); 3772 okay = false; 3773 } finally { 3774 apkFile.delete(); 3775 } 3776 3777 return okay; 3778 } 3779 3780 // Given an actual file content size, consume the post-content padding mandated 3781 // by the tar format. 3782 void skipTarPadding(long size, InputStream instream) throws IOException { 3783 long partial = (size + 512) % 512; 3784 if (partial > 0) { 3785 final int needed = 512 - (int)partial; 3786 byte[] buffer = new byte[needed]; 3787 if (readExactly(instream, buffer, 0, needed) == needed) { 3788 mBytes += needed; 3789 } else throw new IOException("Unexpected EOF in padding"); 3790 } 3791 } 3792 3793 // Returns a policy constant; takes a buffer arg to reduce memory churn 3794 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 3795 throws IOException { 3796 // Fail on suspiciously large manifest files 3797 if (info.size > 64 * 1024) { 3798 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 3799 } 3800 3801 byte[] buffer = new byte[(int) info.size]; 3802 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 3803 mBytes += info.size; 3804 } else throw new IOException("Unexpected EOF in manifest"); 3805 3806 RestorePolicy policy = RestorePolicy.IGNORE; 3807 String[] str = new String[1]; 3808 int offset = 0; 3809 3810 try { 3811 offset = extractLine(buffer, offset, str); 3812 int version = Integer.parseInt(str[0]); 3813 if (version == BACKUP_MANIFEST_VERSION) { 3814 offset = extractLine(buffer, offset, str); 3815 String manifestPackage = str[0]; 3816 // TODO: handle <original-package> 3817 if (manifestPackage.equals(info.packageName)) { 3818 offset = extractLine(buffer, offset, str); 3819 version = Integer.parseInt(str[0]); // app version 3820 offset = extractLine(buffer, offset, str); 3821 int platformVersion = Integer.parseInt(str[0]); 3822 offset = extractLine(buffer, offset, str); 3823 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 3824 offset = extractLine(buffer, offset, str); 3825 boolean hasApk = str[0].equals("1"); 3826 offset = extractLine(buffer, offset, str); 3827 int numSigs = Integer.parseInt(str[0]); 3828 if (numSigs > 0) { 3829 Signature[] sigs = new Signature[numSigs]; 3830 for (int i = 0; i < numSigs; i++) { 3831 offset = extractLine(buffer, offset, str); 3832 sigs[i] = new Signature(str[0]); 3833 } 3834 mManifestSignatures.put(info.packageName, sigs); 3835 3836 // Okay, got the manifest info we need... 3837 try { 3838 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 3839 info.packageName, PackageManager.GET_SIGNATURES); 3840 // Fall through to IGNORE if the app explicitly disallows backup 3841 final int flags = pkgInfo.applicationInfo.flags; 3842 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 3843 // Restore system-uid-space packages only if they have 3844 // defined a custom backup agent 3845 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 3846 || (pkgInfo.applicationInfo.backupAgentName != null)) { 3847 // Verify signatures against any installed version; if they 3848 // don't match, then we fall though and ignore the data. The 3849 // signatureMatch() method explicitly ignores the signature 3850 // check for packages installed on the system partition, because 3851 // such packages are signed with the platform cert instead of 3852 // the app developer's cert, so they're different on every 3853 // device. 3854 if (signaturesMatch(sigs, pkgInfo)) { 3855 if (pkgInfo.versionCode >= version) { 3856 Slog.i(TAG, "Sig + version match; taking data"); 3857 policy = RestorePolicy.ACCEPT; 3858 } else { 3859 // The data is from a newer version of the app than 3860 // is presently installed. That means we can only 3861 // use it if the matching apk is also supplied. 3862 Slog.d(TAG, "Data version " + version 3863 + " is newer than installed version " 3864 + pkgInfo.versionCode + " - requiring apk"); 3865 policy = RestorePolicy.ACCEPT_IF_APK; 3866 } 3867 } else { 3868 Slog.w(TAG, "Restore manifest signatures do not match " 3869 + "installed application for " + info.packageName); 3870 } 3871 } else { 3872 Slog.w(TAG, "Package " + info.packageName 3873 + " is system level with no agent"); 3874 } 3875 } else { 3876 if (DEBUG) Slog.i(TAG, "Restore manifest from " 3877 + info.packageName + " but allowBackup=false"); 3878 } 3879 } catch (NameNotFoundException e) { 3880 // Okay, the target app isn't installed. We can process 3881 // the restore properly only if the dataset provides the 3882 // apk file and we can successfully install it. 3883 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 3884 + " not installed; requiring apk in dataset"); 3885 policy = RestorePolicy.ACCEPT_IF_APK; 3886 } 3887 3888 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 3889 Slog.i(TAG, "Cannot restore package " + info.packageName 3890 + " without the matching .apk"); 3891 } 3892 } else { 3893 Slog.i(TAG, "Missing signature on backed-up package " 3894 + info.packageName); 3895 } 3896 } else { 3897 Slog.i(TAG, "Expected package " + info.packageName 3898 + " but restore manifest claims " + manifestPackage); 3899 } 3900 } else { 3901 Slog.i(TAG, "Unknown restore manifest version " + version 3902 + " for package " + info.packageName); 3903 } 3904 } catch (NumberFormatException e) { 3905 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 3906 } catch (IllegalArgumentException e) { 3907 Slog.w(TAG, e.getMessage()); 3908 } 3909 3910 return policy; 3911 } 3912 3913 // Builds a line from a byte buffer starting at 'offset', and returns 3914 // the index of the next unconsumed data in the buffer. 3915 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 3916 final int end = buffer.length; 3917 if (offset >= end) throw new IOException("Incomplete data"); 3918 3919 int pos; 3920 for (pos = offset; pos < end; pos++) { 3921 byte c = buffer[pos]; 3922 // at LF we declare end of line, and return the next char as the 3923 // starting point for the next time through 3924 if (c == '\n') { 3925 break; 3926 } 3927 } 3928 outStr[0] = new String(buffer, offset, pos - offset); 3929 pos++; // may be pointing an extra byte past the end but that's okay 3930 return pos; 3931 } 3932 3933 void dumpFileMetadata(FileMetadata info) { 3934 if (DEBUG) { 3935 StringBuilder b = new StringBuilder(128); 3936 3937 // mode string 3938 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 3939 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 3940 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 3941 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 3942 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 3943 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 3944 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 3945 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 3946 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 3947 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 3948 b.append(String.format(" %9d ", info.size)); 3949 3950 Date stamp = new Date(info.mtime); 3951 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 3952 3953 b.append(info.packageName); 3954 b.append(" :: "); 3955 b.append(info.domain); 3956 b.append(" :: "); 3957 b.append(info.path); 3958 3959 Slog.i(TAG, b.toString()); 3960 } 3961 } 3962 // Consume a tar file header block [sequence] and accumulate the relevant metadata 3963 FileMetadata readTarHeaders(InputStream instream) throws IOException { 3964 byte[] block = new byte[512]; 3965 FileMetadata info = null; 3966 3967 boolean gotHeader = readTarHeader(instream, block); 3968 if (gotHeader) { 3969 try { 3970 // okay, presume we're okay, and extract the various metadata 3971 info = new FileMetadata(); 3972 info.size = extractRadix(block, 124, 12, 8); 3973 info.mtime = extractRadix(block, 136, 12, 8); 3974 info.mode = extractRadix(block, 100, 8, 8); 3975 3976 info.path = extractString(block, 345, 155); // prefix 3977 String path = extractString(block, 0, 100); 3978 if (path.length() > 0) { 3979 if (info.path.length() > 0) info.path += '/'; 3980 info.path += path; 3981 } 3982 3983 // tar link indicator field: 1 byte at offset 156 in the header. 3984 int typeChar = block[156]; 3985 if (typeChar == 'x') { 3986 // pax extended header, so we need to read that 3987 gotHeader = readPaxExtendedHeader(instream, info); 3988 if (gotHeader) { 3989 // and after a pax extended header comes another real header -- read 3990 // that to find the real file type 3991 gotHeader = readTarHeader(instream, block); 3992 } 3993 if (!gotHeader) throw new IOException("Bad or missing pax header"); 3994 3995 typeChar = block[156]; 3996 } 3997 3998 switch (typeChar) { 3999 case '0': info.type = BackupAgent.TYPE_FILE; break; 4000 case '5': { 4001 info.type = BackupAgent.TYPE_DIRECTORY; 4002 if (info.size != 0) { 4003 Slog.w(TAG, "Directory entry with nonzero size in header"); 4004 info.size = 0; 4005 } 4006 break; 4007 } 4008 case 0: { 4009 // presume EOF 4010 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 4011 return null; 4012 } 4013 default: { 4014 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 4015 throw new IOException("Unknown entity type " + typeChar); 4016 } 4017 } 4018 4019 // Parse out the path 4020 // 4021 // first: apps/shared/unrecognized 4022 if (FullBackup.SHARED_PREFIX.regionMatches(0, 4023 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 4024 // File in shared storage. !!! TODO: implement this. 4025 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 4026 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 4027 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 4028 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 4029 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 4030 info.path, 0, FullBackup.APPS_PREFIX.length())) { 4031 // App content! Parse out the package name and domain 4032 4033 // strip the apps/ prefix 4034 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 4035 4036 // extract the package name 4037 int slash = info.path.indexOf('/'); 4038 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 4039 info.packageName = info.path.substring(0, slash); 4040 info.path = info.path.substring(slash+1); 4041 4042 // if it's a manifest we're done, otherwise parse out the domains 4043 if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) { 4044 slash = info.path.indexOf('/'); 4045 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path); 4046 info.domain = info.path.substring(0, slash); 4047 info.path = info.path.substring(slash + 1); 4048 } 4049 } 4050 } catch (IOException e) { 4051 if (DEBUG) { 4052 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 4053 HEXLOG(block); 4054 } 4055 throw e; 4056 } 4057 } 4058 return info; 4059 } 4060 4061 private void HEXLOG(byte[] block) { 4062 int offset = 0; 4063 int todo = block.length; 4064 StringBuilder buf = new StringBuilder(64); 4065 while (todo > 0) { 4066 buf.append(String.format("%04x ", offset)); 4067 int numThisLine = (todo > 16) ? 16 : todo; 4068 for (int i = 0; i < numThisLine; i++) { 4069 buf.append(String.format("%02x ", block[offset+i])); 4070 } 4071 Slog.i("hexdump", buf.toString()); 4072 buf.setLength(0); 4073 todo -= numThisLine; 4074 offset += numThisLine; 4075 } 4076 } 4077 4078 // Read exactly the given number of bytes into a buffer at the stated offset. 4079 // Returns false if EOF is encountered before the requested number of bytes 4080 // could be read. 4081 int readExactly(InputStream in, byte[] buffer, int offset, int size) 4082 throws IOException { 4083 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 4084 4085 int soFar = 0; 4086 while (soFar < size) { 4087 int nRead = in.read(buffer, offset + soFar, size - soFar); 4088 if (nRead <= 0) { 4089 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 4090 break; 4091 } 4092 soFar += nRead; 4093 } 4094 return soFar; 4095 } 4096 4097 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 4098 final int got = readExactly(instream, block, 0, 512); 4099 if (got == 0) return false; // Clean EOF 4100 if (got < 512) throw new IOException("Unable to read full block header"); 4101 mBytes += 512; 4102 return true; 4103 } 4104 4105 // overwrites 'info' fields based on the pax extended header 4106 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 4107 throws IOException { 4108 // We should never see a pax extended header larger than this 4109 if (info.size > 32*1024) { 4110 Slog.w(TAG, "Suspiciously large pax header size " + info.size 4111 + " - aborting"); 4112 throw new IOException("Sanity failure: pax header size " + info.size); 4113 } 4114 4115 // read whole blocks, not just the content size 4116 int numBlocks = (int)((info.size + 511) >> 9); 4117 byte[] data = new byte[numBlocks * 512]; 4118 if (readExactly(instream, data, 0, data.length) < data.length) { 4119 throw new IOException("Unable to read full pax header"); 4120 } 4121 mBytes += data.length; 4122 4123 final int contentSize = (int) info.size; 4124 int offset = 0; 4125 do { 4126 // extract the line at 'offset' 4127 int eol = offset+1; 4128 while (eol < contentSize && data[eol] != ' ') eol++; 4129 if (eol >= contentSize) { 4130 // error: we just hit EOD looking for the end of the size field 4131 throw new IOException("Invalid pax data"); 4132 } 4133 // eol points to the space between the count and the key 4134 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 4135 int key = eol + 1; // start of key=value 4136 eol = offset + linelen - 1; // trailing LF 4137 int value; 4138 for (value = key+1; data[value] != '=' && value <= eol; value++); 4139 if (value > eol) { 4140 throw new IOException("Invalid pax declaration"); 4141 } 4142 4143 // pax requires that key/value strings be in UTF-8 4144 String keyStr = new String(data, key, value-key, "UTF-8"); 4145 // -1 to strip the trailing LF 4146 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 4147 4148 if ("path".equals(keyStr)) { 4149 info.path = valStr; 4150 } else if ("size".equals(keyStr)) { 4151 info.size = Long.parseLong(valStr); 4152 } else { 4153 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 4154 } 4155 4156 offset += linelen; 4157 } while (offset < contentSize); 4158 4159 return true; 4160 } 4161 4162 long extractRadix(byte[] data, int offset, int maxChars, int radix) 4163 throws IOException { 4164 long value = 0; 4165 final int end = offset + maxChars; 4166 for (int i = offset; i < end; i++) { 4167 final byte b = data[i]; 4168 // Numeric fields in tar can terminate with either NUL or SPC 4169 if (b == 0 || b == ' ') break; 4170 if (b < '0' || b > ('0' + radix - 1)) { 4171 throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix); 4172 } 4173 value = radix * value + (b - '0'); 4174 } 4175 return value; 4176 } 4177 4178 String extractString(byte[] data, int offset, int maxChars) throws IOException { 4179 final int end = offset + maxChars; 4180 int eos = offset; 4181 // tar string fields terminate early with a NUL 4182 while (eos < end && data[eos] != 0) eos++; 4183 return new String(data, offset, eos-offset, "US-ASCII"); 4184 } 4185 4186 void sendStartRestore() { 4187 if (mObserver != null) { 4188 try { 4189 mObserver.onStartRestore(); 4190 } catch (RemoteException e) { 4191 Slog.w(TAG, "full restore observer went away: startRestore"); 4192 mObserver = null; 4193 } 4194 } 4195 } 4196 4197 void sendOnRestorePackage(String name) { 4198 if (mObserver != null) { 4199 try { 4200 // TODO: use a more user-friendly name string 4201 mObserver.onRestorePackage(name); 4202 } catch (RemoteException e) { 4203 Slog.w(TAG, "full restore observer went away: restorePackage"); 4204 mObserver = null; 4205 } 4206 } 4207 } 4208 4209 void sendEndRestore() { 4210 if (mObserver != null) { 4211 try { 4212 mObserver.onEndRestore(); 4213 } catch (RemoteException e) { 4214 Slog.w(TAG, "full restore observer went away: endRestore"); 4215 mObserver = null; 4216 } 4217 } 4218 } 4219 } 4220 4221 // ----- Restore handling ----- 4222 4223 private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 4224 // If the target resides on the system partition, we allow it to restore 4225 // data from the like-named package in a restore set even if the signatures 4226 // do not match. (Unlike general applications, those flashed to the system 4227 // partition will be signed with the device's platform certificate, so on 4228 // different phones the same system app will have different signatures.) 4229 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 4230 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 4231 return true; 4232 } 4233 4234 // Allow unsigned apps, but not signed on one device and unsigned on the other 4235 // !!! TODO: is this the right policy? 4236 Signature[] deviceSigs = target.signatures; 4237 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs 4238 + " device=" + deviceSigs); 4239 if ((storedSigs == null || storedSigs.length == 0) 4240 && (deviceSigs == null || deviceSigs.length == 0)) { 4241 return true; 4242 } 4243 if (storedSigs == null || deviceSigs == null) { 4244 return false; 4245 } 4246 4247 // !!! TODO: this demands that every stored signature match one 4248 // that is present on device, and does not demand the converse. 4249 // Is this this right policy? 4250 int nStored = storedSigs.length; 4251 int nDevice = deviceSigs.length; 4252 4253 for (int i=0; i < nStored; i++) { 4254 boolean match = false; 4255 for (int j=0; j < nDevice; j++) { 4256 if (storedSigs[i].equals(deviceSigs[j])) { 4257 match = true; 4258 break; 4259 } 4260 } 4261 if (!match) { 4262 return false; 4263 } 4264 } 4265 return true; 4266 } 4267 4268 enum RestoreState { 4269 INITIAL, 4270 DOWNLOAD_DATA, 4271 PM_METADATA, 4272 RUNNING_QUEUE, 4273 FINAL 4274 } 4275 4276 class PerformRestoreTask implements BackupRestoreTask { 4277 private IBackupTransport mTransport; 4278 private IRestoreObserver mObserver; 4279 private long mToken; 4280 private PackageInfo mTargetPackage; 4281 private File mStateDir; 4282 private int mPmToken; 4283 private boolean mNeedFullBackup; 4284 private HashSet<String> mFilterSet; 4285 private long mStartRealtime; 4286 private PackageManagerBackupAgent mPmAgent; 4287 private List<PackageInfo> mAgentPackages; 4288 private ArrayList<PackageInfo> mRestorePackages; 4289 private RestoreState mCurrentState; 4290 private int mCount; 4291 private boolean mFinished; 4292 private int mStatus; 4293 private File mBackupDataName; 4294 private File mNewStateName; 4295 private File mSavedStateName; 4296 private ParcelFileDescriptor mBackupData; 4297 private ParcelFileDescriptor mNewState; 4298 private PackageInfo mCurrentPackage; 4299 4300 4301 class RestoreRequest { 4302 public PackageInfo app; 4303 public int storedAppVersion; 4304 4305 RestoreRequest(PackageInfo _app, int _version) { 4306 app = _app; 4307 storedAppVersion = _version; 4308 } 4309 } 4310 4311 PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer, 4312 long restoreSetToken, PackageInfo targetPackage, int pmToken, 4313 boolean needFullBackup, String[] filterSet) { 4314 mCurrentState = RestoreState.INITIAL; 4315 mFinished = false; 4316 mPmAgent = null; 4317 4318 mTransport = transport; 4319 mObserver = observer; 4320 mToken = restoreSetToken; 4321 mTargetPackage = targetPackage; 4322 mPmToken = pmToken; 4323 mNeedFullBackup = needFullBackup; 4324 4325 if (filterSet != null) { 4326 mFilterSet = new HashSet<String>(); 4327 for (String pkg : filterSet) { 4328 mFilterSet.add(pkg); 4329 } 4330 } else { 4331 mFilterSet = null; 4332 } 4333 4334 try { 4335 mStateDir = new File(mBaseStateDir, transport.transportDirName()); 4336 } catch (RemoteException e) { 4337 // can't happen; the transport is local 4338 } 4339 } 4340 4341 // Execute one tick of whatever state machine the task implements 4342 @Override 4343 public void execute() { 4344 if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step: " + mCurrentState); 4345 switch (mCurrentState) { 4346 case INITIAL: 4347 beginRestore(); 4348 break; 4349 4350 case DOWNLOAD_DATA: 4351 downloadRestoreData(); 4352 break; 4353 4354 case PM_METADATA: 4355 restorePmMetadata(); 4356 break; 4357 4358 case RUNNING_QUEUE: 4359 restoreNextAgent(); 4360 break; 4361 4362 case FINAL: 4363 if (!mFinished) finalizeRestore(); 4364 else { 4365 Slog.e(TAG, "Duplicate finish"); 4366 } 4367 mFinished = true; 4368 break; 4369 } 4370 } 4371 4372 // Initialize and set up for the PM metadata restore, which comes first 4373 void beginRestore() { 4374 // Don't account time doing the restore as inactivity of the app 4375 // that has opened a restore session. 4376 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 4377 4378 // Assume error until we successfully init everything 4379 mStatus = BackupConstants.TRANSPORT_ERROR; 4380 4381 try { 4382 // TODO: Log this before getAvailableRestoreSets, somehow 4383 EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken); 4384 4385 // Get the list of all packages which have backup enabled. 4386 // (Include the Package Manager metadata pseudo-package first.) 4387 mRestorePackages = new ArrayList<PackageInfo>(); 4388 PackageInfo omPackage = new PackageInfo(); 4389 omPackage.packageName = PACKAGE_MANAGER_SENTINEL; 4390 mRestorePackages.add(omPackage); 4391 4392 mAgentPackages = allAgentPackages(); 4393 if (mTargetPackage == null) { 4394 // if there's a filter set, strip out anything that isn't 4395 // present before proceeding 4396 if (mFilterSet != null) { 4397 for (int i = mAgentPackages.size() - 1; i >= 0; i--) { 4398 final PackageInfo pkg = mAgentPackages.get(i); 4399 if (! mFilterSet.contains(pkg.packageName)) { 4400 mAgentPackages.remove(i); 4401 } 4402 } 4403 if (MORE_DEBUG) { 4404 Slog.i(TAG, "Post-filter package set for restore:"); 4405 for (PackageInfo p : mAgentPackages) { 4406 Slog.i(TAG, " " + p); 4407 } 4408 } 4409 } 4410 mRestorePackages.addAll(mAgentPackages); 4411 } else { 4412 // Just one package to attempt restore of 4413 mRestorePackages.add(mTargetPackage); 4414 } 4415 4416 // let the observer know that we're running 4417 if (mObserver != null) { 4418 try { 4419 // !!! TODO: get an actual count from the transport after 4420 // its startRestore() runs? 4421 mObserver.restoreStarting(mRestorePackages.size()); 4422 } catch (RemoteException e) { 4423 Slog.d(TAG, "Restore observer died at restoreStarting"); 4424 mObserver = null; 4425 } 4426 } 4427 } catch (RemoteException e) { 4428 // Something has gone catastrophically wrong with the transport 4429 Slog.e(TAG, "Error communicating with transport for restore"); 4430 executeNextState(RestoreState.FINAL); 4431 return; 4432 } 4433 4434 mStatus = BackupConstants.TRANSPORT_OK; 4435 executeNextState(RestoreState.DOWNLOAD_DATA); 4436 } 4437 4438 void downloadRestoreData() { 4439 // Note that the download phase can be very time consuming, but we're executing 4440 // it inline here on the looper. This is "okay" because it is not calling out to 4441 // third party code; the transport is "trusted," and so we assume it is being a 4442 // good citizen and timing out etc when appropriate. 4443 // 4444 // TODO: when appropriate, move the download off the looper and rearrange the 4445 // error handling around that. 4446 try { 4447 mStatus = mTransport.startRestore(mToken, 4448 mRestorePackages.toArray(new PackageInfo[0])); 4449 if (mStatus != BackupConstants.TRANSPORT_OK) { 4450 Slog.e(TAG, "Error starting restore operation"); 4451 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 4452 executeNextState(RestoreState.FINAL); 4453 return; 4454 } 4455 } catch (RemoteException e) { 4456 Slog.e(TAG, "Error communicating with transport for restore"); 4457 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 4458 mStatus = BackupConstants.TRANSPORT_ERROR; 4459 executeNextState(RestoreState.FINAL); 4460 return; 4461 } 4462 4463 // Successful download of the data to be parceled out to the apps, so off we go. 4464 executeNextState(RestoreState.PM_METADATA); 4465 } 4466 4467 void restorePmMetadata() { 4468 try { 4469 String packageName = mTransport.nextRestorePackage(); 4470 if (packageName == null) { 4471 Slog.e(TAG, "Error getting first restore package"); 4472 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 4473 mStatus = BackupConstants.TRANSPORT_ERROR; 4474 executeNextState(RestoreState.FINAL); 4475 return; 4476 } else if (packageName.equals("")) { 4477 Slog.i(TAG, "No restore data available"); 4478 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); 4479 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis); 4480 mStatus = BackupConstants.TRANSPORT_OK; 4481 executeNextState(RestoreState.FINAL); 4482 return; 4483 } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) { 4484 Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL 4485 + "\", found only \"" + packageName + "\""); 4486 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL, 4487 "Package manager data missing"); 4488 executeNextState(RestoreState.FINAL); 4489 return; 4490 } 4491 4492 // Pull the Package Manager metadata from the restore set first 4493 PackageInfo omPackage = new PackageInfo(); 4494 omPackage.packageName = PACKAGE_MANAGER_SENTINEL; 4495 mPmAgent = new PackageManagerBackupAgent( 4496 mPackageManager, mAgentPackages); 4497 initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()), 4498 mNeedFullBackup); 4499 // The PM agent called operationComplete() already, because our invocation 4500 // of it is process-local and therefore synchronous. That means that a 4501 // RUNNING_QUEUE message is already enqueued. Only if we're unable to 4502 // proceed with running the queue do we remove that pending message and 4503 // jump straight to the FINAL state. 4504 4505 // Verify that the backup set includes metadata. If not, we can't do 4506 // signature/version verification etc, so we simply do not proceed with 4507 // the restore operation. 4508 if (!mPmAgent.hasMetadata()) { 4509 Slog.e(TAG, "No restore metadata available, so not restoring settings"); 4510 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL, 4511 "Package manager restore metadata missing"); 4512 mStatus = BackupConstants.TRANSPORT_ERROR; 4513 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 4514 executeNextState(RestoreState.FINAL); 4515 return; 4516 } 4517 } catch (RemoteException e) { 4518 Slog.e(TAG, "Error communicating with transport for restore"); 4519 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 4520 mStatus = BackupConstants.TRANSPORT_ERROR; 4521 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 4522 executeNextState(RestoreState.FINAL); 4523 return; 4524 } 4525 4526 // Metadata is intact, so we can now run the restore queue. If we get here, 4527 // we have already enqueued the necessary next-step message on the looper. 4528 } 4529 4530 void restoreNextAgent() { 4531 try { 4532 String packageName = mTransport.nextRestorePackage(); 4533 4534 if (packageName == null) { 4535 Slog.e(TAG, "Error getting next restore package"); 4536 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 4537 executeNextState(RestoreState.FINAL); 4538 return; 4539 } else if (packageName.equals("")) { 4540 if (DEBUG) Slog.v(TAG, "No next package, finishing restore"); 4541 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); 4542 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis); 4543 executeNextState(RestoreState.FINAL); 4544 return; 4545 } 4546 4547 if (mObserver != null) { 4548 try { 4549 mObserver.onUpdate(mCount, packageName); 4550 } catch (RemoteException e) { 4551 Slog.d(TAG, "Restore observer died in onUpdate"); 4552 mObserver = null; 4553 } 4554 } 4555 4556 Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); 4557 if (metaInfo == null) { 4558 Slog.e(TAG, "Missing metadata for " + packageName); 4559 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 4560 "Package metadata missing"); 4561 executeNextState(RestoreState.RUNNING_QUEUE); 4562 return; 4563 } 4564 4565 PackageInfo packageInfo; 4566 try { 4567 int flags = PackageManager.GET_SIGNATURES; 4568 packageInfo = mPackageManager.getPackageInfo(packageName, flags); 4569 } catch (NameNotFoundException e) { 4570 Slog.e(TAG, "Invalid package restoring data", e); 4571 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 4572 "Package missing on device"); 4573 executeNextState(RestoreState.RUNNING_QUEUE); 4574 return; 4575 } 4576 4577 if (packageInfo.applicationInfo.backupAgentName == null 4578 || "".equals(packageInfo.applicationInfo.backupAgentName)) { 4579 if (DEBUG) { 4580 Slog.i(TAG, "Data exists for package " + packageName 4581 + " but app has no agent; skipping"); 4582 } 4583 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 4584 "Package has no agent"); 4585 executeNextState(RestoreState.RUNNING_QUEUE); 4586 return; 4587 } 4588 4589 if (metaInfo.versionCode > packageInfo.versionCode) { 4590 // Data is from a "newer" version of the app than we have currently 4591 // installed. If the app has not declared that it is prepared to 4592 // handle this case, we do not attempt the restore. 4593 if ((packageInfo.applicationInfo.flags 4594 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { 4595 String message = "Version " + metaInfo.versionCode 4596 + " > installed version " + packageInfo.versionCode; 4597 Slog.w(TAG, "Package " + packageName + ": " + message); 4598 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 4599 packageName, message); 4600 executeNextState(RestoreState.RUNNING_QUEUE); 4601 return; 4602 } else { 4603 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode 4604 + " > installed " + packageInfo.versionCode 4605 + " but restoreAnyVersion"); 4606 } 4607 } 4608 4609 if (!signaturesMatch(metaInfo.signatures, packageInfo)) { 4610 Slog.w(TAG, "Signature mismatch restoring " + packageName); 4611 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 4612 "Signature mismatch"); 4613 executeNextState(RestoreState.RUNNING_QUEUE); 4614 return; 4615 } 4616 4617 if (DEBUG) Slog.v(TAG, "Package " + packageName 4618 + " restore version [" + metaInfo.versionCode 4619 + "] is compatible with installed version [" 4620 + packageInfo.versionCode + "]"); 4621 4622 // Then set up and bind the agent 4623 IBackupAgent agent = bindToAgentSynchronous( 4624 packageInfo.applicationInfo, 4625 IApplicationThread.BACKUP_MODE_INCREMENTAL); 4626 if (agent == null) { 4627 Slog.w(TAG, "Can't find backup agent for " + packageName); 4628 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 4629 "Restore agent missing"); 4630 executeNextState(RestoreState.RUNNING_QUEUE); 4631 return; 4632 } 4633 4634 // And then finally start the restore on this agent 4635 try { 4636 initiateOneRestore(packageInfo, metaInfo.versionCode, agent, mNeedFullBackup); 4637 ++mCount; 4638 } catch (Exception e) { 4639 Slog.e(TAG, "Error when attempting restore: " + e.toString()); 4640 agentErrorCleanup(); 4641 executeNextState(RestoreState.RUNNING_QUEUE); 4642 } 4643 } catch (RemoteException e) { 4644 Slog.e(TAG, "Unable to fetch restore data from transport"); 4645 mStatus = BackupConstants.TRANSPORT_ERROR; 4646 executeNextState(RestoreState.FINAL); 4647 } 4648 } 4649 4650 void finalizeRestore() { 4651 if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver); 4652 4653 try { 4654 mTransport.finishRestore(); 4655 } catch (RemoteException e) { 4656 Slog.e(TAG, "Error finishing restore", e); 4657 } 4658 4659 if (mObserver != null) { 4660 try { 4661 mObserver.restoreFinished(mStatus); 4662 } catch (RemoteException e) { 4663 Slog.d(TAG, "Restore observer died at restoreFinished"); 4664 } 4665 } 4666 4667 // If this was a restoreAll operation, record that this was our 4668 // ancestral dataset, as well as the set of apps that are possibly 4669 // restoreable from the dataset 4670 if (mTargetPackage == null && mPmAgent != null) { 4671 mAncestralPackages = mPmAgent.getRestoredPackages(); 4672 mAncestralToken = mToken; 4673 writeRestoreTokens(); 4674 } 4675 4676 // We must under all circumstances tell the Package Manager to 4677 // proceed with install notifications if it's waiting for us. 4678 if (mPmToken > 0) { 4679 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken); 4680 try { 4681 mPackageManagerBinder.finishPackageInstall(mPmToken); 4682 } catch (RemoteException e) { /* can't happen */ } 4683 } 4684 4685 // Furthermore we need to reset the session timeout clock 4686 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 4687 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, 4688 TIMEOUT_RESTORE_INTERVAL); 4689 4690 // done; we can finally release the wakelock 4691 Slog.i(TAG, "Restore complete."); 4692 mWakelock.release(); 4693 } 4694 4695 // Call asynchronously into the app, passing it the restore data. The next step 4696 // after this is always a callback, either operationComplete() or handleTimeout(). 4697 void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent, 4698 boolean needFullBackup) { 4699 mCurrentPackage = app; 4700 final String packageName = app.packageName; 4701 4702 if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName); 4703 4704 // !!! TODO: get the dirs from the transport 4705 mBackupDataName = new File(mDataDir, packageName + ".restore"); 4706 mNewStateName = new File(mStateDir, packageName + ".new"); 4707 mSavedStateName = new File(mStateDir, packageName); 4708 4709 final int token = generateToken(); 4710 try { 4711 // Run the transport's restore pass 4712 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 4713 ParcelFileDescriptor.MODE_READ_WRITE | 4714 ParcelFileDescriptor.MODE_CREATE | 4715 ParcelFileDescriptor.MODE_TRUNCATE); 4716 4717 if (!SELinux.restorecon(mBackupDataName)) { 4718 Slog.e(TAG, "SElinux restorecon failed for " + mBackupDataName); 4719 } 4720 4721 if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) { 4722 // Transport-level failure, so we wind everything up and 4723 // terminate the restore operation. 4724 Slog.e(TAG, "Error getting restore data for " + packageName); 4725 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 4726 mBackupData.close(); 4727 mBackupDataName.delete(); 4728 executeNextState(RestoreState.FINAL); 4729 return; 4730 } 4731 4732 // Okay, we have the data. Now have the agent do the restore. 4733 mBackupData.close(); 4734 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 4735 ParcelFileDescriptor.MODE_READ_ONLY); 4736 4737 mNewState = ParcelFileDescriptor.open(mNewStateName, 4738 ParcelFileDescriptor.MODE_READ_WRITE | 4739 ParcelFileDescriptor.MODE_CREATE | 4740 ParcelFileDescriptor.MODE_TRUNCATE); 4741 4742 // Kick off the restore, checking for hung agents 4743 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this); 4744 agent.doRestore(mBackupData, appVersionCode, mNewState, token, mBackupManagerBinder); 4745 } catch (Exception e) { 4746 Slog.e(TAG, "Unable to call app for restore: " + packageName, e); 4747 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString()); 4748 agentErrorCleanup(); // clears any pending timeout messages as well 4749 4750 // After a restore failure we go back to running the queue. If there 4751 // are no more packages to be restored that will be handled by the 4752 // next step. 4753 executeNextState(RestoreState.RUNNING_QUEUE); 4754 } 4755 } 4756 4757 void agentErrorCleanup() { 4758 // If the agent fails restore, it might have put the app's data 4759 // into an incoherent state. For consistency we wipe its data 4760 // again in this case before continuing with normal teardown 4761 clearApplicationDataSynchronous(mCurrentPackage.packageName); 4762 agentCleanup(); 4763 } 4764 4765 void agentCleanup() { 4766 mBackupDataName.delete(); 4767 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 4768 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 4769 mBackupData = mNewState = null; 4770 4771 // if everything went okay, remember the recorded state now 4772 // 4773 // !!! TODO: the restored data should be migrated on the server 4774 // side into the current dataset. In that case the new state file 4775 // we just created would reflect the data already extant in the 4776 // backend, so there'd be nothing more to do. Until that happens, 4777 // however, we need to make sure that we record the data to the 4778 // current backend dataset. (Yes, this means shipping the data over 4779 // the wire in both directions. That's bad, but consistency comes 4780 // first, then efficiency.) Once we introduce server-side data 4781 // migration to the newly-restored device's dataset, we will change 4782 // the following from a discard of the newly-written state to the 4783 // "correct" operation of renaming into the canonical state blob. 4784 mNewStateName.delete(); // TODO: remove; see above comment 4785 //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this 4786 4787 // If this wasn't the PM pseudopackage, tear down the agent side 4788 if (mCurrentPackage.applicationInfo != null) { 4789 // unbind and tidy up even on timeout or failure 4790 try { 4791 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 4792 4793 // The agent was probably running with a stub Application object, 4794 // which isn't a valid run mode for the main app logic. Shut 4795 // down the app so that next time it's launched, it gets the 4796 // usual full initialization. Note that this is only done for 4797 // full-system restores: when a single app has requested a restore, 4798 // it is explicitly not killed following that operation. 4799 if (mTargetPackage == null && (mCurrentPackage.applicationInfo.flags 4800 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) { 4801 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of " 4802 + mCurrentPackage.applicationInfo.processName); 4803 mActivityManager.killApplicationProcess( 4804 mCurrentPackage.applicationInfo.processName, 4805 mCurrentPackage.applicationInfo.uid); 4806 } 4807 } catch (RemoteException e) { 4808 // can't happen; we run in the same process as the activity manager 4809 } 4810 } 4811 4812 // The caller is responsible for reestablishing the state machine; our 4813 // responsibility here is to clear the decks for whatever comes next. 4814 mBackupHandler.removeMessages(MSG_TIMEOUT, this); 4815 synchronized (mCurrentOpLock) { 4816 mCurrentOperations.clear(); 4817 } 4818 } 4819 4820 // A call to agent.doRestore() has been positively acknowledged as complete 4821 @Override 4822 public void operationComplete() { 4823 int size = (int) mBackupDataName.length(); 4824 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size); 4825 // Just go back to running the restore queue 4826 agentCleanup(); 4827 4828 executeNextState(RestoreState.RUNNING_QUEUE); 4829 } 4830 4831 // A call to agent.doRestore() has timed out 4832 @Override 4833 public void handleTimeout() { 4834 Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName); 4835 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 4836 mCurrentPackage.packageName, "restore timeout"); 4837 // Handle like an agent that threw on invocation: wipe it and go on to the next 4838 agentErrorCleanup(); 4839 executeNextState(RestoreState.RUNNING_QUEUE); 4840 } 4841 4842 void executeNextState(RestoreState nextState) { 4843 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 4844 + this + " nextState=" + nextState); 4845 mCurrentState = nextState; 4846 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 4847 mBackupHandler.sendMessage(msg); 4848 } 4849 } 4850 4851 class PerformClearTask implements Runnable { 4852 IBackupTransport mTransport; 4853 PackageInfo mPackage; 4854 4855 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) { 4856 mTransport = transport; 4857 mPackage = packageInfo; 4858 } 4859 4860 public void run() { 4861 try { 4862 // Clear the on-device backup state to ensure a full backup next time 4863 File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); 4864 File stateFile = new File(stateDir, mPackage.packageName); 4865 stateFile.delete(); 4866 4867 // Tell the transport to remove all the persistent storage for the app 4868 // TODO - need to handle failures 4869 mTransport.clearBackupData(mPackage); 4870 } catch (RemoteException e) { 4871 // can't happen; the transport is local 4872 } catch (Exception e) { 4873 Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage); 4874 } finally { 4875 try { 4876 // TODO - need to handle failures 4877 mTransport.finishBackup(); 4878 } catch (RemoteException e) { 4879 // can't happen; the transport is local 4880 } 4881 4882 // Last but not least, release the cpu 4883 mWakelock.release(); 4884 } 4885 } 4886 } 4887 4888 class PerformInitializeTask implements Runnable { 4889 HashSet<String> mQueue; 4890 4891 PerformInitializeTask(HashSet<String> transportNames) { 4892 mQueue = transportNames; 4893 } 4894 4895 public void run() { 4896 try { 4897 for (String transportName : mQueue) { 4898 IBackupTransport transport = getTransport(transportName); 4899 if (transport == null) { 4900 Slog.e(TAG, "Requested init for " + transportName + " but not found"); 4901 continue; 4902 } 4903 4904 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); 4905 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName()); 4906 long startRealtime = SystemClock.elapsedRealtime(); 4907 int status = transport.initializeDevice(); 4908 4909 if (status == BackupConstants.TRANSPORT_OK) { 4910 status = transport.finishBackup(); 4911 } 4912 4913 // Okay, the wipe really happened. Clean up our local bookkeeping. 4914 if (status == BackupConstants.TRANSPORT_OK) { 4915 Slog.i(TAG, "Device init successful"); 4916 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); 4917 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 4918 resetBackupState(new File(mBaseStateDir, transport.transportDirName())); 4919 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); 4920 synchronized (mQueueLock) { 4921 recordInitPendingLocked(false, transportName); 4922 } 4923 } else { 4924 // If this didn't work, requeue this one and try again 4925 // after a suitable interval 4926 Slog.e(TAG, "Transport error in initializeDevice()"); 4927 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 4928 synchronized (mQueueLock) { 4929 recordInitPendingLocked(true, transportName); 4930 } 4931 // do this via another alarm to make sure of the wakelock states 4932 long delay = transport.requestBackupTime(); 4933 if (DEBUG) Slog.w(TAG, "init failed on " 4934 + transportName + " resched in " + delay); 4935 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 4936 System.currentTimeMillis() + delay, mRunInitIntent); 4937 } 4938 } 4939 } catch (RemoteException e) { 4940 // can't happen; the transports are local 4941 } catch (Exception e) { 4942 Slog.e(TAG, "Unexpected error performing init", e); 4943 } finally { 4944 // Done; release the wakelock 4945 mWakelock.release(); 4946 } 4947 } 4948 } 4949 4950 private void dataChangedImpl(String packageName) { 4951 HashSet<String> targets = dataChangedTargets(packageName); 4952 dataChangedImpl(packageName, targets); 4953 } 4954 4955 private void dataChangedImpl(String packageName, HashSet<String> targets) { 4956 // Record that we need a backup pass for the caller. Since multiple callers 4957 // may share a uid, we need to note all candidates within that uid and schedule 4958 // a backup pass for each of them. 4959 EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName); 4960 4961 if (targets == null) { 4962 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 4963 + " uid=" + Binder.getCallingUid()); 4964 return; 4965 } 4966 4967 synchronized (mQueueLock) { 4968 // Note that this client has made data changes that need to be backed up 4969 if (targets.contains(packageName)) { 4970 // Add the caller to the set of pending backups. If there is 4971 // one already there, then overwrite it, but no harm done. 4972 BackupRequest req = new BackupRequest(packageName); 4973 if (mPendingBackups.put(packageName, req) == null) { 4974 if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName); 4975 4976 // Journal this request in case of crash. The put() 4977 // operation returned null when this package was not already 4978 // in the set; we want to avoid touching the disk redundantly. 4979 writeToJournalLocked(packageName); 4980 4981 if (MORE_DEBUG) { 4982 int numKeys = mPendingBackups.size(); 4983 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); 4984 for (BackupRequest b : mPendingBackups.values()) { 4985 Slog.d(TAG, " + " + b); 4986 } 4987 } 4988 } 4989 } 4990 } 4991 } 4992 4993 // Note: packageName is currently unused, but may be in the future 4994 private HashSet<String> dataChangedTargets(String packageName) { 4995 // If the caller does not hold the BACKUP permission, it can only request a 4996 // backup of its own data. 4997 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 4998 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 4999 synchronized (mBackupParticipants) { 5000 return mBackupParticipants.get(Binder.getCallingUid()); 5001 } 5002 } 5003 5004 // a caller with full permission can ask to back up any participating app 5005 // !!! TODO: allow backup of ANY app? 5006 HashSet<String> targets = new HashSet<String>(); 5007 synchronized (mBackupParticipants) { 5008 int N = mBackupParticipants.size(); 5009 for (int i = 0; i < N; i++) { 5010 HashSet<String> s = mBackupParticipants.valueAt(i); 5011 if (s != null) { 5012 targets.addAll(s); 5013 } 5014 } 5015 } 5016 return targets; 5017 } 5018 5019 private void writeToJournalLocked(String str) { 5020 RandomAccessFile out = null; 5021 try { 5022 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir); 5023 out = new RandomAccessFile(mJournal, "rws"); 5024 out.seek(out.length()); 5025 out.writeUTF(str); 5026 } catch (IOException e) { 5027 Slog.e(TAG, "Can't write " + str + " to backup journal", e); 5028 mJournal = null; 5029 } finally { 5030 try { if (out != null) out.close(); } catch (IOException e) {} 5031 } 5032 } 5033 5034 // ----- IBackupManager binder interface ----- 5035 5036 public void dataChanged(final String packageName) { 5037 final int callingUserHandle = UserHandle.getCallingUserId(); 5038 if (callingUserHandle != UserHandle.USER_OWNER) { 5039 // App is running under a non-owner user profile. For now, we do not back 5040 // up data from secondary user profiles. 5041 // TODO: backups for all user profiles. 5042 if (MORE_DEBUG) { 5043 Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " 5044 + callingUserHandle); 5045 } 5046 return; 5047 } 5048 5049 final HashSet<String> targets = dataChangedTargets(packageName); 5050 if (targets == null) { 5051 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 5052 + " uid=" + Binder.getCallingUid()); 5053 return; 5054 } 5055 5056 mBackupHandler.post(new Runnable() { 5057 public void run() { 5058 dataChangedImpl(packageName, targets); 5059 } 5060 }); 5061 } 5062 5063 // Clear the given package's backup data from the current transport 5064 public void clearBackupData(String packageName) { 5065 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName); 5066 PackageInfo info; 5067 try { 5068 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 5069 } catch (NameNotFoundException e) { 5070 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); 5071 return; 5072 } 5073 5074 // If the caller does not hold the BACKUP permission, it can only request a 5075 // wipe of its own backed-up data. 5076 HashSet<String> apps; 5077 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 5078 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 5079 apps = mBackupParticipants.get(Binder.getCallingUid()); 5080 } else { 5081 // a caller with full permission can ask to back up any participating app 5082 // !!! TODO: allow data-clear of ANY app? 5083 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps"); 5084 apps = new HashSet<String>(); 5085 int N = mBackupParticipants.size(); 5086 for (int i = 0; i < N; i++) { 5087 HashSet<String> s = mBackupParticipants.valueAt(i); 5088 if (s != null) { 5089 apps.addAll(s); 5090 } 5091 } 5092 } 5093 5094 // Is the given app an available participant? 5095 if (apps.contains(packageName)) { 5096 if (DEBUG) Slog.v(TAG, "Found the app - running clear process"); 5097 // found it; fire off the clear request 5098 synchronized (mQueueLock) { 5099 long oldId = Binder.clearCallingIdentity(); 5100 mWakelock.acquire(); 5101 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, 5102 new ClearParams(getTransport(mCurrentTransport), info)); 5103 mBackupHandler.sendMessage(msg); 5104 Binder.restoreCallingIdentity(oldId); 5105 } 5106 } 5107 } 5108 5109 // Run a backup pass immediately for any applications that have declared 5110 // that they have pending updates. 5111 public void backupNow() { 5112 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow"); 5113 5114 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); 5115 synchronized (mQueueLock) { 5116 // Because the alarms we are using can jitter, and we want an *immediate* 5117 // backup pass to happen, we restart the timer beginning with "next time," 5118 // then manually fire the backup trigger intent ourselves. 5119 startBackupAlarmsLocked(BACKUP_INTERVAL); 5120 try { 5121 mRunBackupIntent.send(); 5122 } catch (PendingIntent.CanceledException e) { 5123 // should never happen 5124 Slog.e(TAG, "run-backup intent cancelled!"); 5125 } 5126 } 5127 } 5128 5129 boolean deviceIsProvisioned() { 5130 final ContentResolver resolver = mContext.getContentResolver(); 5131 return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); 5132 } 5133 5134 // Run a *full* backup pass for the given package, writing the resulting data stream 5135 // to the supplied file descriptor. This method is synchronous and does not return 5136 // to the caller until the backup has been completed. 5137 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, 5138 boolean includeObbs, boolean includeShared, 5139 boolean doAllApps, boolean includeSystem, String[] pkgList) { 5140 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); 5141 5142 final int callingUserHandle = UserHandle.getCallingUserId(); 5143 if (callingUserHandle != UserHandle.USER_OWNER) { 5144 throw new IllegalStateException("Backup supported only for the device owner"); 5145 } 5146 5147 // Validate 5148 if (!doAllApps) { 5149 if (!includeShared) { 5150 // If we're backing up shared data (sdcard or equivalent), then we can run 5151 // without any supplied app names. Otherwise, we'd be doing no work, so 5152 // report the error. 5153 if (pkgList == null || pkgList.length == 0) { 5154 throw new IllegalArgumentException( 5155 "Backup requested but neither shared nor any apps named"); 5156 } 5157 } 5158 } 5159 5160 long oldId = Binder.clearCallingIdentity(); 5161 try { 5162 // Doesn't make sense to do a full backup prior to setup 5163 if (!deviceIsProvisioned()) { 5164 Slog.i(TAG, "Full backup not supported before setup"); 5165 return; 5166 } 5167 5168 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks 5169 + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps 5170 + " pkgs=" + pkgList); 5171 Slog.i(TAG, "Beginning full backup..."); 5172 5173 FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs, 5174 includeShared, doAllApps, includeSystem, pkgList); 5175 final int token = generateToken(); 5176 synchronized (mFullConfirmations) { 5177 mFullConfirmations.put(token, params); 5178 } 5179 5180 // start up the confirmation UI 5181 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token); 5182 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { 5183 Slog.e(TAG, "Unable to launch full backup confirmation"); 5184 mFullConfirmations.delete(token); 5185 return; 5186 } 5187 5188 // make sure the screen is lit for the user interaction 5189 mPowerManager.userActivity(SystemClock.uptimeMillis(), false); 5190 5191 // start the confirmation countdown 5192 startConfirmationTimeout(token, params); 5193 5194 // wait for the backup to be performed 5195 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion..."); 5196 waitForCompletion(params); 5197 } finally { 5198 try { 5199 fd.close(); 5200 } catch (IOException e) { 5201 // just eat it 5202 } 5203 Binder.restoreCallingIdentity(oldId); 5204 Slog.d(TAG, "Full backup processing complete."); 5205 } 5206 } 5207 5208 public void fullRestore(ParcelFileDescriptor fd) { 5209 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); 5210 5211 final int callingUserHandle = UserHandle.getCallingUserId(); 5212 if (callingUserHandle != UserHandle.USER_OWNER) { 5213 throw new IllegalStateException("Restore supported only for the device owner"); 5214 } 5215 5216 long oldId = Binder.clearCallingIdentity(); 5217 5218 try { 5219 // Check whether the device has been provisioned -- we don't handle 5220 // full restores prior to completing the setup process. 5221 if (!deviceIsProvisioned()) { 5222 Slog.i(TAG, "Full restore not permitted before setup"); 5223 return; 5224 } 5225 5226 Slog.i(TAG, "Beginning full restore..."); 5227 5228 FullRestoreParams params = new FullRestoreParams(fd); 5229 final int token = generateToken(); 5230 synchronized (mFullConfirmations) { 5231 mFullConfirmations.put(token, params); 5232 } 5233 5234 // start up the confirmation UI 5235 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token); 5236 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) { 5237 Slog.e(TAG, "Unable to launch full restore confirmation"); 5238 mFullConfirmations.delete(token); 5239 return; 5240 } 5241 5242 // make sure the screen is lit for the user interaction 5243 mPowerManager.userActivity(SystemClock.uptimeMillis(), false); 5244 5245 // start the confirmation countdown 5246 startConfirmationTimeout(token, params); 5247 5248 // wait for the restore to be performed 5249 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion..."); 5250 waitForCompletion(params); 5251 } finally { 5252 try { 5253 fd.close(); 5254 } catch (IOException e) { 5255 Slog.w(TAG, "Error trying to close fd after full restore: " + e); 5256 } 5257 Binder.restoreCallingIdentity(oldId); 5258 Slog.i(TAG, "Full restore processing complete."); 5259 } 5260 } 5261 5262 boolean startConfirmationUi(int token, String action) { 5263 try { 5264 Intent confIntent = new Intent(action); 5265 confIntent.setClassName("com.android.backupconfirm", 5266 "com.android.backupconfirm.BackupRestoreConfirmation"); 5267 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token); 5268 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 5269 mContext.startActivity(confIntent); 5270 } catch (ActivityNotFoundException e) { 5271 return false; 5272 } 5273 return true; 5274 } 5275 5276 void startConfirmationTimeout(int token, FullParams params) { 5277 if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after " 5278 + TIMEOUT_FULL_CONFIRMATION + " millis"); 5279 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, 5280 token, 0, params); 5281 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION); 5282 } 5283 5284 void waitForCompletion(FullParams params) { 5285 synchronized (params.latch) { 5286 while (params.latch.get() == false) { 5287 try { 5288 params.latch.wait(); 5289 } catch (InterruptedException e) { /* never interrupted */ } 5290 } 5291 } 5292 } 5293 5294 void signalFullBackupRestoreCompletion(FullParams params) { 5295 synchronized (params.latch) { 5296 params.latch.set(true); 5297 params.latch.notifyAll(); 5298 } 5299 } 5300 5301 // Confirm that the previously-requested full backup/restore operation can proceed. This 5302 // is used to require a user-facing disclosure about the operation. 5303 @Override 5304 public void acknowledgeFullBackupOrRestore(int token, boolean allow, 5305 String curPassword, String encPpassword, IFullBackupRestoreObserver observer) { 5306 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token 5307 + " allow=" + allow); 5308 5309 // TODO: possibly require not just this signature-only permission, but even 5310 // require that the specific designated confirmation-UI app uid is the caller? 5311 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore"); 5312 5313 long oldId = Binder.clearCallingIdentity(); 5314 try { 5315 5316 FullParams params; 5317 synchronized (mFullConfirmations) { 5318 params = mFullConfirmations.get(token); 5319 if (params != null) { 5320 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params); 5321 mFullConfirmations.delete(token); 5322 5323 if (allow) { 5324 final int verb = params instanceof FullBackupParams 5325 ? MSG_RUN_FULL_BACKUP 5326 : MSG_RUN_FULL_RESTORE; 5327 5328 params.observer = observer; 5329 params.curPassword = curPassword; 5330 5331 boolean isEncrypted; 5332 try { 5333 isEncrypted = (mMountService.getEncryptionState() != MountService.ENCRYPTION_STATE_NONE); 5334 if (isEncrypted) Slog.w(TAG, "Device is encrypted; forcing enc password"); 5335 } catch (RemoteException e) { 5336 // couldn't contact the mount service; fail "safe" and assume encryption 5337 Slog.e(TAG, "Unable to contact mount service!"); 5338 isEncrypted = true; 5339 } 5340 params.encryptPassword = (isEncrypted) ? curPassword : encPpassword; 5341 5342 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb); 5343 mWakelock.acquire(); 5344 Message msg = mBackupHandler.obtainMessage(verb, params); 5345 mBackupHandler.sendMessage(msg); 5346 } else { 5347 Slog.w(TAG, "User rejected full backup/restore operation"); 5348 // indicate completion without having actually transferred any data 5349 signalFullBackupRestoreCompletion(params); 5350 } 5351 } else { 5352 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token"); 5353 } 5354 } 5355 } finally { 5356 Binder.restoreCallingIdentity(oldId); 5357 } 5358 } 5359 5360 // Enable/disable the backup service 5361 public void setBackupEnabled(boolean enable) { 5362 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5363 "setBackupEnabled"); 5364 5365 Slog.i(TAG, "Backup enabled => " + enable); 5366 5367 boolean wasEnabled = mEnabled; 5368 synchronized (this) { 5369 Settings.Secure.putInt(mContext.getContentResolver(), 5370 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0); 5371 mEnabled = enable; 5372 } 5373 5374 synchronized (mQueueLock) { 5375 if (enable && !wasEnabled && mProvisioned) { 5376 // if we've just been enabled, start scheduling backup passes 5377 startBackupAlarmsLocked(BACKUP_INTERVAL); 5378 } else if (!enable) { 5379 // No longer enabled, so stop running backups 5380 if (DEBUG) Slog.i(TAG, "Opting out of backup"); 5381 5382 mAlarmManager.cancel(mRunBackupIntent); 5383 5384 // This also constitutes an opt-out, so we wipe any data for 5385 // this device from the backend. We start that process with 5386 // an alarm in order to guarantee wakelock states. 5387 if (wasEnabled && mProvisioned) { 5388 // NOTE: we currently flush every registered transport, not just 5389 // the currently-active one. 5390 HashSet<String> allTransports; 5391 synchronized (mTransports) { 5392 allTransports = new HashSet<String>(mTransports.keySet()); 5393 } 5394 // build the set of transports for which we are posting an init 5395 for (String transport : allTransports) { 5396 recordInitPendingLocked(true, transport); 5397 } 5398 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 5399 mRunInitIntent); 5400 } 5401 } 5402 } 5403 } 5404 5405 // Enable/disable automatic restore of app data at install time 5406 public void setAutoRestore(boolean doAutoRestore) { 5407 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5408 "setAutoRestore"); 5409 5410 Slog.i(TAG, "Auto restore => " + doAutoRestore); 5411 5412 synchronized (this) { 5413 Settings.Secure.putInt(mContext.getContentResolver(), 5414 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0); 5415 mAutoRestore = doAutoRestore; 5416 } 5417 } 5418 5419 // Mark the backup service as having been provisioned 5420 public void setBackupProvisioned(boolean available) { 5421 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5422 "setBackupProvisioned"); 5423 /* 5424 * This is now a no-op; provisioning is simply the device's own setup state. 5425 */ 5426 } 5427 5428 private void startBackupAlarmsLocked(long delayBeforeFirstBackup) { 5429 // We used to use setInexactRepeating(), but that may be linked to 5430 // backups running at :00 more often than not, creating load spikes. 5431 // Schedule at an exact time for now, and also add a bit of "fuzz". 5432 5433 Random random = new Random(); 5434 long when = System.currentTimeMillis() + delayBeforeFirstBackup + 5435 random.nextInt(FUZZ_MILLIS); 5436 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when, 5437 BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent); 5438 mNextBackupPass = when; 5439 } 5440 5441 // Report whether the backup mechanism is currently enabled 5442 public boolean isBackupEnabled() { 5443 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); 5444 return mEnabled; // no need to synchronize just to read it 5445 } 5446 5447 // Report the name of the currently active transport 5448 public String getCurrentTransport() { 5449 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5450 "getCurrentTransport"); 5451 if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); 5452 return mCurrentTransport; 5453 } 5454 5455 // Report all known, available backup transports 5456 public String[] listAllTransports() { 5457 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); 5458 5459 String[] list = null; 5460 ArrayList<String> known = new ArrayList<String>(); 5461 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { 5462 if (entry.getValue() != null) { 5463 known.add(entry.getKey()); 5464 } 5465 } 5466 5467 if (known.size() > 0) { 5468 list = new String[known.size()]; 5469 known.toArray(list); 5470 } 5471 return list; 5472 } 5473 5474 // Select which transport to use for the next backup operation. If the given 5475 // name is not one of the available transports, no action is taken and the method 5476 // returns null. 5477 public String selectBackupTransport(String transport) { 5478 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport"); 5479 5480 synchronized (mTransports) { 5481 String prevTransport = null; 5482 if (mTransports.get(transport) != null) { 5483 prevTransport = mCurrentTransport; 5484 mCurrentTransport = transport; 5485 Settings.Secure.putString(mContext.getContentResolver(), 5486 Settings.Secure.BACKUP_TRANSPORT, transport); 5487 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport 5488 + " returning " + prevTransport); 5489 } else { 5490 Slog.w(TAG, "Attempt to select unavailable transport " + transport); 5491 } 5492 return prevTransport; 5493 } 5494 } 5495 5496 // Supply the configuration Intent for the given transport. If the name is not one 5497 // of the available transports, or if the transport does not supply any configuration 5498 // UI, the method returns null. 5499 public Intent getConfigurationIntent(String transportName) { 5500 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5501 "getConfigurationIntent"); 5502 5503 synchronized (mTransports) { 5504 final IBackupTransport transport = mTransports.get(transportName); 5505 if (transport != null) { 5506 try { 5507 final Intent intent = transport.configurationIntent(); 5508 if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent " 5509 + intent); 5510 return intent; 5511 } catch (RemoteException e) { 5512 /* fall through to return null */ 5513 } 5514 } 5515 } 5516 5517 return null; 5518 } 5519 5520 // Supply the configuration summary string for the given transport. If the name is 5521 // not one of the available transports, or if the transport does not supply any 5522 // summary / destination string, the method can return null. 5523 // 5524 // This string is used VERBATIM as the summary text of the relevant Settings item! 5525 public String getDestinationString(String transportName) { 5526 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5527 "getDestinationString"); 5528 5529 synchronized (mTransports) { 5530 final IBackupTransport transport = mTransports.get(transportName); 5531 if (transport != null) { 5532 try { 5533 final String text = transport.currentDestinationString(); 5534 if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text); 5535 return text; 5536 } catch (RemoteException e) { 5537 /* fall through to return null */ 5538 } 5539 } 5540 } 5541 5542 return null; 5543 } 5544 5545 // Callback: a requested backup agent has been instantiated. This should only 5546 // be called from the Activity Manager. 5547 public void agentConnected(String packageName, IBinder agentBinder) { 5548 synchronized(mAgentConnectLock) { 5549 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 5550 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); 5551 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); 5552 mConnectedAgent = agent; 5553 mConnecting = false; 5554 } else { 5555 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 5556 + " claiming agent connected"); 5557 } 5558 mAgentConnectLock.notifyAll(); 5559 } 5560 } 5561 5562 // Callback: a backup agent has failed to come up, or has unexpectedly quit. 5563 // If the agent failed to come up in the first place, the agentBinder argument 5564 // will be null. This should only be called from the Activity Manager. 5565 public void agentDisconnected(String packageName) { 5566 // TODO: handle backup being interrupted 5567 synchronized(mAgentConnectLock) { 5568 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 5569 mConnectedAgent = null; 5570 mConnecting = false; 5571 } else { 5572 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 5573 + " claiming agent disconnected"); 5574 } 5575 mAgentConnectLock.notifyAll(); 5576 } 5577 } 5578 5579 // An application being installed will need a restore pass, then the Package Manager 5580 // will need to be told when the restore is finished. 5581 public void restoreAtInstall(String packageName, int token) { 5582 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 5583 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 5584 + " attemping install-time restore"); 5585 return; 5586 } 5587 5588 long restoreSet = getAvailableRestoreToken(packageName); 5589 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName 5590 + " token=" + Integer.toHexString(token) 5591 + " restoreSet=" + Long.toHexString(restoreSet)); 5592 5593 if (mAutoRestore && mProvisioned && restoreSet != 0) { 5594 // okay, we're going to attempt a restore of this package from this restore set. 5595 // The eventual message back into the Package Manager to run the post-install 5596 // steps for 'token' will be issued from the restore handling code. 5597 5598 // We can use a synthetic PackageInfo here because: 5599 // 1. We know it's valid, since the Package Manager supplied the name 5600 // 2. Only the packageName field will be used by the restore code 5601 PackageInfo pkg = new PackageInfo(); 5602 pkg.packageName = packageName; 5603 5604 mWakelock.acquire(); 5605 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 5606 msg.obj = new RestoreParams(getTransport(mCurrentTransport), null, 5607 restoreSet, pkg, token, true); 5608 mBackupHandler.sendMessage(msg); 5609 } else { 5610 // Auto-restore disabled or no way to attempt a restore; just tell the Package 5611 // Manager to proceed with the post-install handling for this package. 5612 if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore"); 5613 try { 5614 mPackageManagerBinder.finishPackageInstall(token); 5615 } catch (RemoteException e) { /* can't happen */ } 5616 } 5617 } 5618 5619 // Hand off a restore session 5620 public IRestoreSession beginRestoreSession(String packageName, String transport) { 5621 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName 5622 + " transport=" + transport); 5623 5624 boolean needPermission = true; 5625 if (transport == null) { 5626 transport = mCurrentTransport; 5627 5628 if (packageName != null) { 5629 PackageInfo app = null; 5630 try { 5631 app = mPackageManager.getPackageInfo(packageName, 0); 5632 } catch (NameNotFoundException nnf) { 5633 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 5634 throw new IllegalArgumentException("Package " + packageName + " not found"); 5635 } 5636 5637 if (app.applicationInfo.uid == Binder.getCallingUid()) { 5638 // So: using the current active transport, and the caller has asked 5639 // that its own package will be restored. In this narrow use case 5640 // we do not require the caller to hold the permission. 5641 needPermission = false; 5642 } 5643 } 5644 } 5645 5646 if (needPermission) { 5647 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5648 "beginRestoreSession"); 5649 } else { 5650 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed"); 5651 } 5652 5653 synchronized(this) { 5654 if (mActiveRestoreSession != null) { 5655 Slog.d(TAG, "Restore session requested but one already active"); 5656 return null; 5657 } 5658 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport); 5659 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 5660 } 5661 return mActiveRestoreSession; 5662 } 5663 5664 void clearRestoreSession(ActiveRestoreSession currentSession) { 5665 synchronized(this) { 5666 if (currentSession != mActiveRestoreSession) { 5667 Slog.e(TAG, "ending non-current restore session"); 5668 } else { 5669 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout"); 5670 mActiveRestoreSession = null; 5671 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 5672 } 5673 } 5674 } 5675 5676 // Note that a currently-active backup agent has notified us that it has 5677 // completed the given outstanding asynchronous backup/restore operation. 5678 @Override 5679 public void opComplete(int token) { 5680 if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token)); 5681 Operation op = null; 5682 synchronized (mCurrentOpLock) { 5683 op = mCurrentOperations.get(token); 5684 if (op != null) { 5685 op.state = OP_ACKNOWLEDGED; 5686 } 5687 mCurrentOpLock.notifyAll(); 5688 } 5689 5690 // The completion callback, if any, is invoked on the handler 5691 if (op != null && op.callback != null) { 5692 Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback); 5693 mBackupHandler.sendMessage(msg); 5694 } 5695 } 5696 5697 // ----- Restore session ----- 5698 5699 class ActiveRestoreSession extends IRestoreSession.Stub { 5700 private static final String TAG = "RestoreSession"; 5701 5702 private String mPackageName; 5703 private IBackupTransport mRestoreTransport = null; 5704 RestoreSet[] mRestoreSets = null; 5705 boolean mEnded = false; 5706 5707 ActiveRestoreSession(String packageName, String transport) { 5708 mPackageName = packageName; 5709 mRestoreTransport = getTransport(transport); 5710 } 5711 5712 // --- Binder interface --- 5713 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) { 5714 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5715 "getAvailableRestoreSets"); 5716 if (observer == null) { 5717 throw new IllegalArgumentException("Observer must not be null"); 5718 } 5719 5720 if (mEnded) { 5721 throw new IllegalStateException("Restore session already ended"); 5722 } 5723 5724 long oldId = Binder.clearCallingIdentity(); 5725 try { 5726 if (mRestoreTransport == null) { 5727 Slog.w(TAG, "Null transport getting restore sets"); 5728 return -1; 5729 } 5730 // spin off the transport request to our service thread 5731 mWakelock.acquire(); 5732 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS, 5733 new RestoreGetSetsParams(mRestoreTransport, this, observer)); 5734 mBackupHandler.sendMessage(msg); 5735 return 0; 5736 } catch (Exception e) { 5737 Slog.e(TAG, "Error in getAvailableRestoreSets", e); 5738 return -1; 5739 } finally { 5740 Binder.restoreCallingIdentity(oldId); 5741 } 5742 } 5743 5744 public synchronized int restoreAll(long token, IRestoreObserver observer) { 5745 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5746 "performRestore"); 5747 5748 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token) 5749 + " observer=" + observer); 5750 5751 if (mEnded) { 5752 throw new IllegalStateException("Restore session already ended"); 5753 } 5754 5755 if (mRestoreTransport == null || mRestoreSets == null) { 5756 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 5757 return -1; 5758 } 5759 5760 if (mPackageName != null) { 5761 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 5762 return -1; 5763 } 5764 5765 synchronized (mQueueLock) { 5766 for (int i = 0; i < mRestoreSets.length; i++) { 5767 if (token == mRestoreSets[i].token) { 5768 long oldId = Binder.clearCallingIdentity(); 5769 mWakelock.acquire(); 5770 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 5771 msg.obj = new RestoreParams(mRestoreTransport, observer, token, true); 5772 mBackupHandler.sendMessage(msg); 5773 Binder.restoreCallingIdentity(oldId); 5774 return 0; 5775 } 5776 } 5777 } 5778 5779 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 5780 return -1; 5781 } 5782 5783 public synchronized int restoreSome(long token, IRestoreObserver observer, 5784 String[] packages) { 5785 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 5786 "performRestore"); 5787 5788 if (DEBUG) { 5789 StringBuilder b = new StringBuilder(128); 5790 b.append("restoreSome token="); 5791 b.append(Long.toHexString(token)); 5792 b.append(" observer="); 5793 b.append(observer.toString()); 5794 b.append(" packages="); 5795 if (packages == null) { 5796 b.append("null"); 5797 } else { 5798 b.append('{'); 5799 boolean first = true; 5800 for (String s : packages) { 5801 if (!first) { 5802 b.append(", "); 5803 } else first = false; 5804 b.append(s); 5805 } 5806 b.append('}'); 5807 } 5808 Slog.d(TAG, b.toString()); 5809 } 5810 5811 if (mEnded) { 5812 throw new IllegalStateException("Restore session already ended"); 5813 } 5814 5815 if (mRestoreTransport == null || mRestoreSets == null) { 5816 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 5817 return -1; 5818 } 5819 5820 if (mPackageName != null) { 5821 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 5822 return -1; 5823 } 5824 5825 synchronized (mQueueLock) { 5826 for (int i = 0; i < mRestoreSets.length; i++) { 5827 if (token == mRestoreSets[i].token) { 5828 long oldId = Binder.clearCallingIdentity(); 5829 mWakelock.acquire(); 5830 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 5831 msg.obj = new RestoreParams(mRestoreTransport, observer, token, 5832 packages, true); 5833 mBackupHandler.sendMessage(msg); 5834 Binder.restoreCallingIdentity(oldId); 5835 return 0; 5836 } 5837 } 5838 } 5839 5840 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 5841 return -1; 5842 } 5843 5844 public synchronized int restorePackage(String packageName, IRestoreObserver observer) { 5845 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); 5846 5847 if (mEnded) { 5848 throw new IllegalStateException("Restore session already ended"); 5849 } 5850 5851 if (mPackageName != null) { 5852 if (! mPackageName.equals(packageName)) { 5853 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName 5854 + " on session for package " + mPackageName); 5855 return -1; 5856 } 5857 } 5858 5859 PackageInfo app = null; 5860 try { 5861 app = mPackageManager.getPackageInfo(packageName, 0); 5862 } catch (NameNotFoundException nnf) { 5863 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 5864 return -1; 5865 } 5866 5867 // If the caller is not privileged and is not coming from the target 5868 // app's uid, throw a permission exception back to the caller. 5869 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP, 5870 Binder.getCallingPid(), Binder.getCallingUid()); 5871 if ((perm == PackageManager.PERMISSION_DENIED) && 5872 (app.applicationInfo.uid != Binder.getCallingUid())) { 5873 Slog.w(TAG, "restorePackage: bad packageName=" + packageName 5874 + " or calling uid=" + Binder.getCallingUid()); 5875 throw new SecurityException("No permission to restore other packages"); 5876 } 5877 5878 // If the package has no backup agent, we obviously cannot proceed 5879 if (app.applicationInfo.backupAgentName == null) { 5880 Slog.w(TAG, "Asked to restore package " + packageName + " with no agent"); 5881 return -1; 5882 } 5883 5884 // So far so good; we're allowed to try to restore this package. Now 5885 // check whether there is data for it in the current dataset, falling back 5886 // to the ancestral dataset if not. 5887 long token = getAvailableRestoreToken(packageName); 5888 5889 // If we didn't come up with a place to look -- no ancestral dataset and 5890 // the app has never been backed up from this device -- there's nothing 5891 // to do but return failure. 5892 if (token == 0) { 5893 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring"); 5894 return -1; 5895 } 5896 5897 // Ready to go: enqueue the restore request and claim success 5898 long oldId = Binder.clearCallingIdentity(); 5899 mWakelock.acquire(); 5900 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 5901 msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0, false); 5902 mBackupHandler.sendMessage(msg); 5903 Binder.restoreCallingIdentity(oldId); 5904 return 0; 5905 } 5906 5907 // Posted to the handler to tear down a restore session in a cleanly synchronized way 5908 class EndRestoreRunnable implements Runnable { 5909 BackupManagerService mBackupManager; 5910 ActiveRestoreSession mSession; 5911 5912 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) { 5913 mBackupManager = manager; 5914 mSession = session; 5915 } 5916 5917 public void run() { 5918 // clean up the session's bookkeeping 5919 synchronized (mSession) { 5920 try { 5921 if (mSession.mRestoreTransport != null) { 5922 mSession.mRestoreTransport.finishRestore(); 5923 } 5924 } catch (Exception e) { 5925 Slog.e(TAG, "Error in finishRestore", e); 5926 } finally { 5927 mSession.mRestoreTransport = null; 5928 mSession.mEnded = true; 5929 } 5930 } 5931 5932 // clean up the BackupManagerService side of the bookkeeping 5933 // and cancel any pending timeout message 5934 mBackupManager.clearRestoreSession(mSession); 5935 } 5936 } 5937 5938 public synchronized void endRestoreSession() { 5939 if (DEBUG) Slog.d(TAG, "endRestoreSession"); 5940 5941 if (mEnded) { 5942 throw new IllegalStateException("Restore session already ended"); 5943 } 5944 5945 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this)); 5946 } 5947 } 5948 5949 @Override 5950 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 5951 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 5952 5953 long identityToken = Binder.clearCallingIdentity(); 5954 try { 5955 dumpInternal(pw); 5956 } finally { 5957 Binder.restoreCallingIdentity(identityToken); 5958 } 5959 } 5960 5961 private void dumpInternal(PrintWriter pw) { 5962 synchronized (mQueueLock) { 5963 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") 5964 + " / " + (!mProvisioned ? "not " : "") + "provisioned / " 5965 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); 5966 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); 5967 if (mBackupRunning) pw.println("Backup currently running"); 5968 pw.println("Last backup pass started: " + mLastBackupPass 5969 + " (now = " + System.currentTimeMillis() + ')'); 5970 pw.println(" next scheduled: " + mNextBackupPass); 5971 5972 pw.println("Available transports:"); 5973 for (String t : listAllTransports()) { 5974 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t); 5975 try { 5976 IBackupTransport transport = getTransport(t); 5977 File dir = new File(mBaseStateDir, transport.transportDirName()); 5978 pw.println(" destination: " + transport.currentDestinationString()); 5979 pw.println(" intent: " + transport.configurationIntent()); 5980 for (File f : dir.listFiles()) { 5981 pw.println(" " + f.getName() + " - " + f.length() + " state bytes"); 5982 } 5983 } catch (Exception e) { 5984 Slog.e(TAG, "Error in transport", e); 5985 pw.println(" Error: " + e); 5986 } 5987 } 5988 5989 pw.println("Pending init: " + mPendingInits.size()); 5990 for (String s : mPendingInits) { 5991 pw.println(" " + s); 5992 } 5993 5994 if (DEBUG_BACKUP_TRACE) { 5995 synchronized (mBackupTrace) { 5996 if (!mBackupTrace.isEmpty()) { 5997 pw.println("Most recent backup trace:"); 5998 for (String s : mBackupTrace) { 5999 pw.println(" " + s); 6000 } 6001 } 6002 } 6003 } 6004 6005 int N = mBackupParticipants.size(); 6006 pw.println("Participants:"); 6007 for (int i=0; i<N; i++) { 6008 int uid = mBackupParticipants.keyAt(i); 6009 pw.print(" uid: "); 6010 pw.println(uid); 6011 HashSet<String> participants = mBackupParticipants.valueAt(i); 6012 for (String app: participants) { 6013 pw.println(" " + app); 6014 } 6015 } 6016 6017 pw.println("Ancestral packages: " 6018 + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); 6019 if (mAncestralPackages != null) { 6020 for (String pkg : mAncestralPackages) { 6021 pw.println(" " + pkg); 6022 } 6023 } 6024 6025 pw.println("Ever backed up: " + mEverStoredApps.size()); 6026 for (String pkg : mEverStoredApps) { 6027 pw.println(" " + pkg); 6028 } 6029 6030 pw.println("Pending backup: " + mPendingBackups.size()); 6031 for (BackupRequest req : mPendingBackups.values()) { 6032 pw.println(" " + req); 6033 } 6034 } 6035 } 6036 } 6037