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