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