BackupManagerService.java revision a7e47d5d344674f4174919c4ac51e5b9c74b5802
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.BackupDataInput; 28import android.app.backup.BackupDataOutput; 29import android.app.backup.BackupTransport; 30import android.app.backup.FullBackup; 31import android.app.backup.RestoreDescription; 32import android.app.backup.RestoreSet; 33import android.app.backup.IBackupManager; 34import android.app.backup.IFullBackupRestoreObserver; 35import android.app.backup.IRestoreObserver; 36import android.app.backup.IRestoreSession; 37import android.content.ActivityNotFoundException; 38import android.content.BroadcastReceiver; 39import android.content.ComponentName; 40import android.content.ContentResolver; 41import android.content.Context; 42import android.content.Intent; 43import android.content.IntentFilter; 44import android.content.ServiceConnection; 45import android.content.pm.ApplicationInfo; 46import android.content.pm.IPackageDataObserver; 47import android.content.pm.IPackageDeleteObserver; 48import android.content.pm.IPackageInstallObserver; 49import android.content.pm.IPackageManager; 50import android.content.pm.PackageInfo; 51import android.content.pm.PackageManager; 52import android.content.pm.ResolveInfo; 53import android.content.pm.ServiceInfo; 54import android.content.pm.Signature; 55import android.content.pm.PackageManager.NameNotFoundException; 56import android.database.ContentObserver; 57import android.net.Uri; 58import android.os.Binder; 59import android.os.Build; 60import android.os.Bundle; 61import android.os.Environment; 62import android.os.Handler; 63import android.os.HandlerThread; 64import android.os.IBinder; 65import android.os.Looper; 66import android.os.Message; 67import android.os.ParcelFileDescriptor; 68import android.os.PowerManager; 69import android.os.Process; 70import android.os.RemoteException; 71import android.os.SELinux; 72import android.os.ServiceManager; 73import android.os.SystemClock; 74import android.os.UserHandle; 75import android.os.WorkSource; 76import android.os.Environment.UserEnvironment; 77import android.os.storage.IMountService; 78import android.os.storage.StorageManager; 79import android.provider.Settings; 80import android.system.ErrnoException; 81import android.system.Os; 82import android.util.ArrayMap; 83import android.util.AtomicFile; 84import android.util.EventLog; 85import android.util.Log; 86import android.util.Slog; 87import android.util.SparseArray; 88import android.util.StringBuilderPrinter; 89 90import com.android.internal.backup.IBackupTransport; 91import com.android.internal.backup.IObbBackupService; 92import com.android.server.AppWidgetBackupBridge; 93import com.android.server.EventLogTags; 94import com.android.server.SystemService; 95import com.android.server.backup.PackageManagerBackupAgent.Metadata; 96 97import java.io.BufferedInputStream; 98import java.io.BufferedOutputStream; 99import java.io.ByteArrayInputStream; 100import java.io.ByteArrayOutputStream; 101import java.io.DataInputStream; 102import java.io.DataOutputStream; 103import java.io.EOFException; 104import java.io.File; 105import java.io.FileDescriptor; 106import java.io.FileInputStream; 107import java.io.FileNotFoundException; 108import java.io.FileOutputStream; 109import java.io.IOException; 110import java.io.InputStream; 111import java.io.OutputStream; 112import java.io.PrintWriter; 113import java.io.RandomAccessFile; 114import java.security.InvalidAlgorithmParameterException; 115import java.security.InvalidKeyException; 116import java.security.Key; 117import java.security.MessageDigest; 118import java.security.NoSuchAlgorithmException; 119import java.security.SecureRandom; 120import java.security.spec.InvalidKeySpecException; 121import java.security.spec.KeySpec; 122import java.text.SimpleDateFormat; 123import java.util.ArrayList; 124import java.util.Arrays; 125import java.util.Collections; 126import java.util.Date; 127import java.util.HashMap; 128import java.util.HashSet; 129import java.util.Iterator; 130import java.util.List; 131import java.util.Map; 132import java.util.Map.Entry; 133import java.util.Random; 134import java.util.Set; 135import java.util.TreeMap; 136import java.util.concurrent.atomic.AtomicBoolean; 137import java.util.concurrent.atomic.AtomicInteger; 138import java.util.zip.Deflater; 139import java.util.zip.DeflaterOutputStream; 140import java.util.zip.InflaterInputStream; 141 142import javax.crypto.BadPaddingException; 143import javax.crypto.Cipher; 144import javax.crypto.CipherInputStream; 145import javax.crypto.CipherOutputStream; 146import javax.crypto.IllegalBlockSizeException; 147import javax.crypto.NoSuchPaddingException; 148import javax.crypto.SecretKey; 149import javax.crypto.SecretKeyFactory; 150import javax.crypto.spec.IvParameterSpec; 151import javax.crypto.spec.PBEKeySpec; 152import javax.crypto.spec.SecretKeySpec; 153 154import libcore.io.IoUtils; 155 156public class BackupManagerService { 157 158 private static final String TAG = "BackupManagerService"; 159 private static final boolean DEBUG = true; 160 private static final boolean MORE_DEBUG = false; 161 private static final boolean DEBUG_SCHEDULING = MORE_DEBUG || true; 162 163 // System-private key used for backing up an app's widget state. Must 164 // begin with U+FFxx by convention (we reserve all keys starting 165 // with U+FF00 or higher for system use). 166 static final String KEY_WIDGET_STATE = "\uffed\uffedwidget"; 167 168 // Historical and current algorithm names 169 static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1"; 170 static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit"; 171 172 // Name and current contents version of the full-backup manifest file 173 // 174 // Manifest version history: 175 // 176 // 1 : initial release 177 static final String BACKUP_MANIFEST_FILENAME = "_manifest"; 178 static final int BACKUP_MANIFEST_VERSION = 1; 179 180 // External archive format version history: 181 // 182 // 1 : initial release 183 // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection 184 // 3 : introduced "_meta" metadata file; no other format change per se 185 static final int BACKUP_FILE_VERSION = 3; 186 static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n"; 187 static final int BACKUP_PW_FILE_VERSION = 2; 188 static final String BACKUP_METADATA_FILENAME = "_meta"; 189 static final int BACKUP_METADATA_VERSION = 1; 190 static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01; 191 static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production 192 193 static final String SETTINGS_PACKAGE = "com.android.providers.settings"; 194 static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup"; 195 static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST"; 196 197 // How often we perform a backup pass. Privileged external callers can 198 // trigger an immediate pass. 199 private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR; 200 201 // Random variation in backup scheduling time to avoid server load spikes 202 private static final int FUZZ_MILLIS = 5 * 60 * 1000; 203 204 // The amount of time between the initial provisioning of the device and 205 // the first backup pass. 206 private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR; 207 208 // Retry interval for clear/init when the transport is unavailable 209 private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR; 210 211 private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN"; 212 private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT"; 213 private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR"; 214 private static final int MSG_RUN_BACKUP = 1; 215 private static final int MSG_RUN_ADB_BACKUP = 2; 216 private static final int MSG_RUN_RESTORE = 3; 217 private static final int MSG_RUN_CLEAR = 4; 218 private static final int MSG_RUN_INITIALIZE = 5; 219 private static final int MSG_RUN_GET_RESTORE_SETS = 6; 220 private static final int MSG_TIMEOUT = 7; 221 private static final int MSG_RESTORE_TIMEOUT = 8; 222 private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9; 223 private static final int MSG_RUN_ADB_RESTORE = 10; 224 private static final int MSG_RETRY_INIT = 11; 225 private static final int MSG_RETRY_CLEAR = 12; 226 private static final int MSG_WIDGET_BROADCAST = 13; 227 private static final int MSG_RUN_FULL_TRANSPORT_BACKUP = 14; 228 229 // backup task state machine tick 230 static final int MSG_BACKUP_RESTORE_STEP = 20; 231 static final int MSG_OP_COMPLETE = 21; 232 233 // Timeout interval for deciding that a bind or clear-data has taken too long 234 static final long TIMEOUT_INTERVAL = 10 * 1000; 235 236 // Timeout intervals for agent backup & restore operations 237 static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000; 238 static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000; 239 static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000; 240 static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000; 241 static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000; 242 243 // User confirmation timeout for a full backup/restore operation. It's this long in 244 // order to give them time to enter the backup password. 245 static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000; 246 247 // How long between attempts to perform a full-data backup of any given app 248 static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day 249 250 Context mContext; 251 private PackageManager mPackageManager; 252 IPackageManager mPackageManagerBinder; 253 private IActivityManager mActivityManager; 254 private PowerManager mPowerManager; 255 private AlarmManager mAlarmManager; 256 private IMountService mMountService; 257 IBackupManager mBackupManagerBinder; 258 259 boolean mEnabled; // access to this is synchronized on 'this' 260 boolean mProvisioned; 261 boolean mAutoRestore; 262 PowerManager.WakeLock mWakelock; 263 HandlerThread mHandlerThread; 264 BackupHandler mBackupHandler; 265 PendingIntent mRunBackupIntent, mRunInitIntent; 266 BroadcastReceiver mRunBackupReceiver, mRunInitReceiver; 267 // map UIDs to the set of participating packages under that UID 268 final SparseArray<HashSet<String>> mBackupParticipants 269 = new SparseArray<HashSet<String>>(); 270 // set of backup services that have pending changes 271 class BackupRequest { 272 public String packageName; 273 274 BackupRequest(String pkgName) { 275 packageName = pkgName; 276 } 277 278 public String toString() { 279 return "BackupRequest{pkg=" + packageName + "}"; 280 } 281 } 282 // Backups that we haven't started yet. Keys are package names. 283 HashMap<String,BackupRequest> mPendingBackups 284 = new HashMap<String,BackupRequest>(); 285 286 // Pseudoname that we use for the Package Manager metadata "package" 287 static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; 288 289 // locking around the pending-backup management 290 final Object mQueueLock = new Object(); 291 292 // The thread performing the sequence of queued backups binds to each app's agent 293 // in succession. Bind notifications are asynchronously delivered through the 294 // Activity Manager; use this lock object to signal when a requested binding has 295 // completed. 296 final Object mAgentConnectLock = new Object(); 297 IBackupAgent mConnectedAgent; 298 volatile boolean mBackupRunning; 299 volatile boolean mConnecting; 300 volatile long mLastBackupPass; 301 volatile long mNextBackupPass; 302 303 // For debugging, we maintain a progress trace of operations during backup 304 static final boolean DEBUG_BACKUP_TRACE = true; 305 final List<String> mBackupTrace = new ArrayList<String>(); 306 307 // A similar synchronization mechanism around clearing apps' data for restore 308 final Object mClearDataLock = new Object(); 309 volatile boolean mClearingData; 310 311 // Transport bookkeeping 312 final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST); 313 final ArrayMap<String,String> mTransportNames 314 = new ArrayMap<String,String>(); // component name -> registration name 315 final ArrayMap<String,IBackupTransport> mTransports 316 = new ArrayMap<String,IBackupTransport>(); // registration name -> binder 317 final ArrayMap<String,TransportConnection> mTransportConnections 318 = new ArrayMap<String,TransportConnection>(); 319 String mCurrentTransport; 320 ActiveRestoreSession mActiveRestoreSession; 321 322 // Watch the device provisioning operation during setup 323 ContentObserver mProvisionedObserver; 324 325 // The published binder is actually to a singleton trampoline object that calls 326 // through to the proper code. This indirection lets us turn down the heavy 327 // implementation object on the fly without disturbing binders that have been 328 // cached elsewhere in the system. 329 static Trampoline sInstance; 330 static Trampoline getInstance() { 331 // Always constructed during system bringup, so no need to lazy-init 332 return sInstance; 333 } 334 335 public static final class Lifecycle extends SystemService { 336 337 public Lifecycle(Context context) { 338 super(context); 339 sInstance = new Trampoline(context); 340 } 341 342 @Override 343 public void onStart() { 344 publishBinderService(Context.BACKUP_SERVICE, sInstance); 345 } 346 347 @Override 348 public void onBootPhase(int phase) { 349 if (phase == PHASE_SYSTEM_SERVICES_READY) { 350 sInstance.initialize(UserHandle.USER_OWNER); 351 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 352 ContentResolver r = sInstance.mContext.getContentResolver(); 353 boolean areEnabled = Settings.Secure.getInt(r, 354 Settings.Secure.BACKUP_ENABLED, 0) != 0; 355 try { 356 sInstance.setBackupEnabled(areEnabled); 357 } catch (RemoteException e) { 358 // can't happen; it's a local object 359 } 360 } 361 } 362 } 363 364 class ProvisionedObserver extends ContentObserver { 365 public ProvisionedObserver(Handler handler) { 366 super(handler); 367 } 368 369 public void onChange(boolean selfChange) { 370 final boolean wasProvisioned = mProvisioned; 371 final boolean isProvisioned = deviceIsProvisioned(); 372 // latch: never unprovision 373 mProvisioned = wasProvisioned || isProvisioned; 374 if (MORE_DEBUG) { 375 Slog.d(TAG, "Provisioning change: was=" + wasProvisioned 376 + " is=" + isProvisioned + " now=" + mProvisioned); 377 } 378 379 synchronized (mQueueLock) { 380 if (mProvisioned && !wasProvisioned && mEnabled) { 381 // we're now good to go, so start the backup alarms 382 if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups"); 383 startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL); 384 } 385 } 386 } 387 } 388 389 class RestoreGetSetsParams { 390 public IBackupTransport transport; 391 public ActiveRestoreSession session; 392 public IRestoreObserver observer; 393 394 RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session, 395 IRestoreObserver _observer) { 396 transport = _transport; 397 session = _session; 398 observer = _observer; 399 } 400 } 401 402 class RestoreParams { 403 public IBackupTransport transport; 404 public String dirName; 405 public IRestoreObserver observer; 406 public long token; 407 public PackageInfo pkgInfo; 408 public int pmToken; // in post-install restore, the PM's token for this transaction 409 public boolean isSystemRestore; 410 public String[] filterSet; 411 412 // Restore a single package 413 RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, 414 long _token, PackageInfo _pkg, int _pmToken) { 415 transport = _transport; 416 dirName = _dirName; 417 observer = _obs; 418 token = _token; 419 pkgInfo = _pkg; 420 pmToken = _pmToken; 421 isSystemRestore = false; 422 filterSet = null; 423 } 424 425 // Restore everything possible. This is the form that Setup Wizard or similar 426 // restore UXes use. 427 RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, 428 long _token) { 429 transport = _transport; 430 dirName = _dirName; 431 observer = _obs; 432 token = _token; 433 pkgInfo = null; 434 pmToken = 0; 435 isSystemRestore = true; 436 filterSet = null; 437 } 438 439 // Restore some set of packages. Leave this one up to the caller to specify 440 // whether it's to be considered a system-level restore. 441 RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, 442 long _token, String[] _filterSet, boolean _isSystemRestore) { 443 transport = _transport; 444 dirName = _dirName; 445 observer = _obs; 446 token = _token; 447 pkgInfo = null; 448 pmToken = 0; 449 isSystemRestore = _isSystemRestore; 450 filterSet = _filterSet; 451 } 452 } 453 454 class ClearParams { 455 public IBackupTransport transport; 456 public PackageInfo packageInfo; 457 458 ClearParams(IBackupTransport _transport, PackageInfo _info) { 459 transport = _transport; 460 packageInfo = _info; 461 } 462 } 463 464 class ClearRetryParams { 465 public String transportName; 466 public String packageName; 467 468 ClearRetryParams(String transport, String pkg) { 469 transportName = transport; 470 packageName = pkg; 471 } 472 } 473 474 class FullParams { 475 public ParcelFileDescriptor fd; 476 public final AtomicBoolean latch; 477 public IFullBackupRestoreObserver observer; 478 public String curPassword; // filled in by the confirmation step 479 public String encryptPassword; 480 481 FullParams() { 482 latch = new AtomicBoolean(false); 483 } 484 } 485 486 class FullBackupParams extends FullParams { 487 public boolean includeApks; 488 public boolean includeObbs; 489 public boolean includeShared; 490 public boolean doWidgets; 491 public boolean allApps; 492 public boolean includeSystem; 493 public boolean doCompress; 494 public String[] packages; 495 496 FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs, 497 boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem, 498 boolean compress, String[] pkgList) { 499 fd = output; 500 includeApks = saveApks; 501 includeObbs = saveObbs; 502 includeShared = saveShared; 503 doWidgets = alsoWidgets; 504 allApps = doAllApps; 505 includeSystem = doSystem; 506 doCompress = compress; 507 packages = pkgList; 508 } 509 } 510 511 class FullRestoreParams extends FullParams { 512 FullRestoreParams(ParcelFileDescriptor input) { 513 fd = input; 514 } 515 } 516 517 // Bookkeeping of in-flight operations for timeout etc. purposes. The operation 518 // token is the index of the entry in the pending-operations list. 519 static final int OP_PENDING = 0; 520 static final int OP_ACKNOWLEDGED = 1; 521 static final int OP_TIMEOUT = -1; 522 523 class Operation { 524 public int state; 525 public BackupRestoreTask callback; 526 527 Operation(int initialState, BackupRestoreTask callbackObj) { 528 state = initialState; 529 callback = callbackObj; 530 } 531 } 532 final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>(); 533 final Object mCurrentOpLock = new Object(); 534 final Random mTokenGenerator = new Random(); 535 536 final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>(); 537 538 // Where we keep our journal files and other bookkeeping 539 File mBaseStateDir; 540 File mDataDir; 541 File mJournalDir; 542 File mJournal; 543 544 // Backup password, if any, and the file where it's saved. What is stored is not the 545 // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but 546 // persisted) salt. Validation is performed by running the challenge text through the 547 // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches 548 // the saved hash string, then the challenge text matches the originally supplied 549 // password text. 550 private final SecureRandom mRng = new SecureRandom(); 551 private String mPasswordHash; 552 private File mPasswordHashFile; 553 private int mPasswordVersion; 554 private File mPasswordVersionFile; 555 private byte[] mPasswordSalt; 556 557 // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys 558 static final int PBKDF2_HASH_ROUNDS = 10000; 559 static final int PBKDF2_KEY_SIZE = 256; // bits 560 static final int PBKDF2_SALT_SIZE = 512; // bits 561 static final String ENCRYPTION_ALGORITHM_NAME = "AES-256"; 562 563 // Keep a log of all the apps we've ever backed up, and what the 564 // dataset tokens are for both the current backup dataset and 565 // the ancestral dataset. 566 private File mEverStored; 567 HashSet<String> mEverStoredApps = new HashSet<String>(); 568 569 static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes 570 File mTokenFile; 571 Set<String> mAncestralPackages = null; 572 long mAncestralToken = 0; 573 long mCurrentToken = 0; 574 575 // Persistently track the need to do a full init 576 static final String INIT_SENTINEL_FILE_NAME = "_need_init_"; 577 HashSet<String> mPendingInits = new HashSet<String>(); // transport names 578 579 // Round-robin queue for scheduling full backup passes 580 static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file 581 class FullBackupEntry implements Comparable<FullBackupEntry> { 582 String packageName; 583 long lastBackup; 584 585 FullBackupEntry(String pkg, long when) { 586 packageName = pkg; 587 lastBackup = when; 588 } 589 590 @Override 591 public int compareTo(FullBackupEntry other) { 592 if (lastBackup < other.lastBackup) return -1; 593 else if (lastBackup > other.lastBackup) return 1; 594 else return 0; 595 } 596 } 597 598 File mFullBackupScheduleFile; 599 // If we're running a schedule-driven full backup, this is the task instance doing it 600 PerformFullTransportBackupTask mRunningFullBackupTask; // inside mQueueLock 601 ArrayList<FullBackupEntry> mFullBackupQueue; // inside mQueueLock 602 603 // Utility: build a new random integer token 604 int generateToken() { 605 int token; 606 do { 607 synchronized (mTokenGenerator) { 608 token = mTokenGenerator.nextInt(); 609 } 610 } while (token < 0); 611 return token; 612 } 613 614 // High level policy: apps are ineligible for backup if certain conditions apply 615 public static boolean appIsEligibleForBackup(ApplicationInfo app) { 616 // 1. their manifest states android:allowBackup="false" 617 if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 618 return false; 619 } 620 621 // 2. they run as a system-level uid but do not supply their own backup agent 622 if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) { 623 return false; 624 } 625 626 // 3. it is the special shared-storage backup package used for 'adb backup' 627 if (app.packageName.equals(BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE)) { 628 return false; 629 } 630 631 return true; 632 } 633 634 /* does *not* check overall backup eligibility policy! */ 635 public static boolean appGetsFullBackup(PackageInfo pkg) { 636 if (pkg.applicationInfo.backupAgentName != null) { 637 // If it has an agent, it gets full backups only if it says so 638 return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0; 639 } 640 641 // No agent means we do full backups for it 642 return true; 643 } 644 645 // ----- Asynchronous backup/restore handler thread ----- 646 647 private class BackupHandler extends Handler { 648 public BackupHandler(Looper looper) { 649 super(looper); 650 } 651 652 public void handleMessage(Message msg) { 653 654 switch (msg.what) { 655 case MSG_RUN_BACKUP: 656 { 657 mLastBackupPass = System.currentTimeMillis(); 658 mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL; 659 660 IBackupTransport transport = getTransport(mCurrentTransport); 661 if (transport == null) { 662 Slog.v(TAG, "Backup requested but no transport available"); 663 synchronized (mQueueLock) { 664 mBackupRunning = false; 665 } 666 mWakelock.release(); 667 break; 668 } 669 670 // snapshot the pending-backup set and work on that 671 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>(); 672 File oldJournal = mJournal; 673 synchronized (mQueueLock) { 674 // Do we have any work to do? Construct the work queue 675 // then release the synchronization lock to actually run 676 // the backup. 677 if (mPendingBackups.size() > 0) { 678 for (BackupRequest b: mPendingBackups.values()) { 679 queue.add(b); 680 } 681 if (DEBUG) Slog.v(TAG, "clearing pending backups"); 682 mPendingBackups.clear(); 683 684 // Start a new backup-queue journal file too 685 mJournal = null; 686 687 } 688 } 689 690 // At this point, we have started a new journal file, and the old 691 // file identity is being passed to the backup processing task. 692 // When it completes successfully, that old journal file will be 693 // deleted. If we crash prior to that, the old journal is parsed 694 // at next boot and the journaled requests fulfilled. 695 boolean staged = true; 696 if (queue.size() > 0) { 697 // Spin up a backup state sequence and set it running 698 try { 699 String dirName = transport.transportDirName(); 700 PerformBackupTask pbt = new PerformBackupTask(transport, dirName, 701 queue, oldJournal); 702 Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt); 703 sendMessage(pbtMessage); 704 } catch (RemoteException e) { 705 // unable to ask the transport its dir name -- transient failure, since 706 // the above check succeeded. Try again next time. 707 Slog.e(TAG, "Transport became unavailable attempting backup"); 708 staged = false; 709 } 710 } else { 711 Slog.v(TAG, "Backup requested but nothing pending"); 712 staged = false; 713 } 714 715 if (!staged) { 716 // if we didn't actually hand off the wakelock, rewind until next time 717 synchronized (mQueueLock) { 718 mBackupRunning = false; 719 } 720 mWakelock.release(); 721 } 722 break; 723 } 724 725 case MSG_BACKUP_RESTORE_STEP: 726 { 727 try { 728 BackupRestoreTask task = (BackupRestoreTask) msg.obj; 729 if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing"); 730 task.execute(); 731 } catch (ClassCastException e) { 732 Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj); 733 } 734 break; 735 } 736 737 case MSG_OP_COMPLETE: 738 { 739 try { 740 BackupRestoreTask task = (BackupRestoreTask) msg.obj; 741 task.operationComplete(); 742 } catch (ClassCastException e) { 743 Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj); 744 } 745 break; 746 } 747 748 case MSG_RUN_ADB_BACKUP: 749 { 750 // TODO: refactor full backup to be a looper-based state machine 751 // similar to normal backup/restore. 752 FullBackupParams params = (FullBackupParams)msg.obj; 753 PerformAdbBackupTask task = new PerformAdbBackupTask(params.fd, 754 params.observer, params.includeApks, params.includeObbs, 755 params.includeShared, params.doWidgets, 756 params.curPassword, params.encryptPassword, 757 params.allApps, params.includeSystem, params.doCompress, 758 params.packages, params.latch); 759 (new Thread(task, "adb-backup")).start(); 760 break; 761 } 762 763 case MSG_RUN_FULL_TRANSPORT_BACKUP: 764 { 765 PerformFullTransportBackupTask task = (PerformFullTransportBackupTask) msg.obj; 766 (new Thread(task, "transport-backup")).start(); 767 break; 768 } 769 770 case MSG_RUN_RESTORE: 771 { 772 RestoreParams params = (RestoreParams)msg.obj; 773 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); 774 BackupRestoreTask task = new PerformUnifiedRestoreTask(params.transport, 775 params.observer, params.token, params.pkgInfo, params.pmToken, 776 params.isSystemRestore, params.filterSet); 777 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task); 778 sendMessage(restoreMsg); 779 break; 780 } 781 782 case MSG_RUN_ADB_RESTORE: 783 { 784 // TODO: refactor full restore to be a looper-based state machine 785 // similar to normal backup/restore. 786 FullRestoreParams params = (FullRestoreParams)msg.obj; 787 PerformAdbRestoreTask task = new PerformAdbRestoreTask(params.fd, 788 params.curPassword, params.encryptPassword, 789 params.observer, params.latch); 790 (new Thread(task, "adb-restore")).start(); 791 break; 792 } 793 794 case MSG_RUN_CLEAR: 795 { 796 ClearParams params = (ClearParams)msg.obj; 797 (new PerformClearTask(params.transport, params.packageInfo)).run(); 798 break; 799 } 800 801 case MSG_RETRY_CLEAR: 802 { 803 // reenqueues if the transport remains unavailable 804 ClearRetryParams params = (ClearRetryParams)msg.obj; 805 clearBackupData(params.transportName, params.packageName); 806 break; 807 } 808 809 case MSG_RUN_INITIALIZE: 810 { 811 HashSet<String> queue; 812 813 // Snapshot the pending-init queue and work on that 814 synchronized (mQueueLock) { 815 queue = new HashSet<String>(mPendingInits); 816 mPendingInits.clear(); 817 } 818 819 (new PerformInitializeTask(queue)).run(); 820 break; 821 } 822 823 case MSG_RETRY_INIT: 824 { 825 synchronized (mQueueLock) { 826 recordInitPendingLocked(msg.arg1 != 0, (String)msg.obj); 827 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 828 mRunInitIntent); 829 } 830 break; 831 } 832 833 case MSG_RUN_GET_RESTORE_SETS: 834 { 835 // Like other async operations, this is entered with the wakelock held 836 RestoreSet[] sets = null; 837 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj; 838 try { 839 sets = params.transport.getAvailableRestoreSets(); 840 // cache the result in the active session 841 synchronized (params.session) { 842 params.session.mRestoreSets = sets; 843 } 844 if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 845 } catch (Exception e) { 846 Slog.e(TAG, "Error from transport getting set list"); 847 } finally { 848 if (params.observer != null) { 849 try { 850 params.observer.restoreSetsAvailable(sets); 851 } catch (RemoteException re) { 852 Slog.e(TAG, "Unable to report listing to observer"); 853 } catch (Exception e) { 854 Slog.e(TAG, "Restore observer threw", e); 855 } 856 } 857 858 // Done: reset the session timeout clock 859 removeMessages(MSG_RESTORE_TIMEOUT); 860 sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 861 862 mWakelock.release(); 863 } 864 break; 865 } 866 867 case MSG_TIMEOUT: 868 { 869 handleTimeout(msg.arg1, msg.obj); 870 break; 871 } 872 873 case MSG_RESTORE_TIMEOUT: 874 { 875 synchronized (BackupManagerService.this) { 876 if (mActiveRestoreSession != null) { 877 // Client app left the restore session dangling. We know that it 878 // can't be in the middle of an actual restore operation because 879 // the timeout is suspended while a restore is in progress. Clean 880 // up now. 881 Slog.w(TAG, "Restore session timed out; aborting"); 882 mActiveRestoreSession.markTimedOut(); 883 post(mActiveRestoreSession.new EndRestoreRunnable( 884 BackupManagerService.this, mActiveRestoreSession)); 885 } 886 } 887 break; 888 } 889 890 case MSG_FULL_CONFIRMATION_TIMEOUT: 891 { 892 synchronized (mFullConfirmations) { 893 FullParams params = mFullConfirmations.get(msg.arg1); 894 if (params != null) { 895 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation"); 896 897 // Release the waiter; timeout == completion 898 signalFullBackupRestoreCompletion(params); 899 900 // Remove the token from the set 901 mFullConfirmations.delete(msg.arg1); 902 903 // Report a timeout to the observer, if any 904 if (params.observer != null) { 905 try { 906 params.observer.onTimeout(); 907 } catch (RemoteException e) { 908 /* don't care if the app has gone away */ 909 } 910 } 911 } else { 912 Slog.d(TAG, "couldn't find params for token " + msg.arg1); 913 } 914 } 915 break; 916 } 917 918 case MSG_WIDGET_BROADCAST: 919 { 920 final Intent intent = (Intent) msg.obj; 921 mContext.sendBroadcastAsUser(intent, UserHandle.OWNER); 922 break; 923 } 924 } 925 } 926 } 927 928 // ----- Debug-only backup operation trace ----- 929 void addBackupTrace(String s) { 930 if (DEBUG_BACKUP_TRACE) { 931 synchronized (mBackupTrace) { 932 mBackupTrace.add(s); 933 } 934 } 935 } 936 937 void clearBackupTrace() { 938 if (DEBUG_BACKUP_TRACE) { 939 synchronized (mBackupTrace) { 940 mBackupTrace.clear(); 941 } 942 } 943 } 944 945 // ----- Main service implementation ----- 946 947 public BackupManagerService(Context context, Trampoline parent) { 948 mContext = context; 949 mPackageManager = context.getPackageManager(); 950 mPackageManagerBinder = AppGlobals.getPackageManager(); 951 mActivityManager = ActivityManagerNative.getDefault(); 952 953 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 954 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 955 mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); 956 957 mBackupManagerBinder = Trampoline.asInterface(parent.asBinder()); 958 959 // spin up the backup/restore handler thread 960 mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND); 961 mHandlerThread.start(); 962 mBackupHandler = new BackupHandler(mHandlerThread.getLooper()); 963 964 // Set up our bookkeeping 965 final ContentResolver resolver = context.getContentResolver(); 966 mProvisioned = Settings.Global.getInt(resolver, 967 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 968 mAutoRestore = Settings.Secure.getInt(resolver, 969 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0; 970 971 mProvisionedObserver = new ProvisionedObserver(mBackupHandler); 972 resolver.registerContentObserver( 973 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), 974 false, mProvisionedObserver); 975 976 // If Encrypted file systems is enabled or disabled, this call will return the 977 // correct directory. 978 mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup"); 979 mBaseStateDir.mkdirs(); 980 if (!SELinux.restorecon(mBaseStateDir)) { 981 Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir); 982 } 983 mDataDir = Environment.getDownloadCacheDirectory(); 984 985 mPasswordVersion = 1; // unless we hear otherwise 986 mPasswordVersionFile = new File(mBaseStateDir, "pwversion"); 987 if (mPasswordVersionFile.exists()) { 988 FileInputStream fin = null; 989 DataInputStream in = null; 990 try { 991 fin = new FileInputStream(mPasswordVersionFile); 992 in = new DataInputStream(fin); 993 mPasswordVersion = in.readInt(); 994 } catch (IOException e) { 995 Slog.e(TAG, "Unable to read backup pw version"); 996 } finally { 997 try { 998 if (in != null) in.close(); 999 if (fin != null) fin.close(); 1000 } catch (IOException e) { 1001 Slog.w(TAG, "Error closing pw version files"); 1002 } 1003 } 1004 } 1005 1006 mPasswordHashFile = new File(mBaseStateDir, "pwhash"); 1007 if (mPasswordHashFile.exists()) { 1008 FileInputStream fin = null; 1009 DataInputStream in = null; 1010 try { 1011 fin = new FileInputStream(mPasswordHashFile); 1012 in = new DataInputStream(new BufferedInputStream(fin)); 1013 // integer length of the salt array, followed by the salt, 1014 // then the hex pw hash string 1015 int saltLen = in.readInt(); 1016 byte[] salt = new byte[saltLen]; 1017 in.readFully(salt); 1018 mPasswordHash = in.readUTF(); 1019 mPasswordSalt = salt; 1020 } catch (IOException e) { 1021 Slog.e(TAG, "Unable to read saved backup pw hash"); 1022 } finally { 1023 try { 1024 if (in != null) in.close(); 1025 if (fin != null) fin.close(); 1026 } catch (IOException e) { 1027 Slog.w(TAG, "Unable to close streams"); 1028 } 1029 } 1030 } 1031 1032 // Alarm receivers for scheduled backups & initialization operations 1033 mRunBackupReceiver = new RunBackupReceiver(); 1034 IntentFilter filter = new IntentFilter(); 1035 filter.addAction(RUN_BACKUP_ACTION); 1036 context.registerReceiver(mRunBackupReceiver, filter, 1037 android.Manifest.permission.BACKUP, null); 1038 1039 mRunInitReceiver = new RunInitializeReceiver(); 1040 filter = new IntentFilter(); 1041 filter.addAction(RUN_INITIALIZE_ACTION); 1042 context.registerReceiver(mRunInitReceiver, filter, 1043 android.Manifest.permission.BACKUP, null); 1044 1045 Intent backupIntent = new Intent(RUN_BACKUP_ACTION); 1046 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 1047 mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0); 1048 1049 Intent initIntent = new Intent(RUN_INITIALIZE_ACTION); 1050 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 1051 mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0); 1052 1053 // Set up the backup-request journaling 1054 mJournalDir = new File(mBaseStateDir, "pending"); 1055 mJournalDir.mkdirs(); // creates mBaseStateDir along the way 1056 mJournal = null; // will be created on first use 1057 1058 // Set up the various sorts of package tracking we do 1059 mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule"); 1060 initPackageTracking(); 1061 1062 // Build our mapping of uid to backup client services. This implicitly 1063 // schedules a backup pass on the Package Manager metadata the first 1064 // time anything needs to be backed up. 1065 synchronized (mBackupParticipants) { 1066 addPackageParticipantsLocked(null); 1067 } 1068 1069 // Set up our transport options and initialize the default transport 1070 // TODO: Don't create transports that we don't need to? 1071 mCurrentTransport = Settings.Secure.getString(context.getContentResolver(), 1072 Settings.Secure.BACKUP_TRANSPORT); 1073 if ("".equals(mCurrentTransport)) { 1074 mCurrentTransport = null; 1075 } 1076 if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport); 1077 1078 // Find all transport hosts and bind to their services 1079 List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser( 1080 mTransportServiceIntent, 0, UserHandle.USER_OWNER); 1081 if (DEBUG) { 1082 Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size())); 1083 } 1084 if (hosts != null) { 1085 for (int i = 0; i < hosts.size(); i++) { 1086 final ServiceInfo transport = hosts.get(i).serviceInfo; 1087 if (MORE_DEBUG) { 1088 Slog.v(TAG, " " + transport.packageName + "/" + transport.name); 1089 } 1090 tryBindTransport(transport); 1091 } 1092 } 1093 1094 // Now that we know about valid backup participants, parse any 1095 // leftover journal files into the pending backup set 1096 parseLeftoverJournals(); 1097 1098 // Power management 1099 mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"); 1100 } 1101 1102 private class RunBackupReceiver extends BroadcastReceiver { 1103 public void onReceive(Context context, Intent intent) { 1104 if (RUN_BACKUP_ACTION.equals(intent.getAction())) { 1105 synchronized (mQueueLock) { 1106 if (mPendingInits.size() > 0) { 1107 // If there are pending init operations, we process those 1108 // and then settle into the usual periodic backup schedule. 1109 if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup"); 1110 try { 1111 mAlarmManager.cancel(mRunInitIntent); 1112 mRunInitIntent.send(); 1113 } catch (PendingIntent.CanceledException ce) { 1114 Slog.e(TAG, "Run init intent cancelled"); 1115 // can't really do more than bail here 1116 } 1117 } else { 1118 // Don't run backups now if we're disabled or not yet 1119 // fully set up. 1120 if (mEnabled && mProvisioned) { 1121 if (!mBackupRunning) { 1122 if (DEBUG) Slog.v(TAG, "Running a backup pass"); 1123 1124 // Acquire the wakelock and pass it to the backup thread. it will 1125 // be released once backup concludes. 1126 mBackupRunning = true; 1127 mWakelock.acquire(); 1128 1129 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP); 1130 mBackupHandler.sendMessage(msg); 1131 } else { 1132 Slog.i(TAG, "Backup time but one already running"); 1133 } 1134 } else { 1135 Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned); 1136 } 1137 } 1138 } 1139 } 1140 } 1141 } 1142 1143 private class RunInitializeReceiver extends BroadcastReceiver { 1144 public void onReceive(Context context, Intent intent) { 1145 if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) { 1146 synchronized (mQueueLock) { 1147 if (DEBUG) Slog.v(TAG, "Running a device init"); 1148 1149 // Acquire the wakelock and pass it to the init thread. it will 1150 // be released once init concludes. 1151 mWakelock.acquire(); 1152 1153 Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE); 1154 mBackupHandler.sendMessage(msg); 1155 } 1156 } 1157 } 1158 } 1159 1160 private void initPackageTracking() { 1161 if (MORE_DEBUG) Slog.v(TAG, "` tracking"); 1162 1163 // Remember our ancestral dataset 1164 mTokenFile = new File(mBaseStateDir, "ancestral"); 1165 try { 1166 RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r"); 1167 int version = tf.readInt(); 1168 if (version == CURRENT_ANCESTRAL_RECORD_VERSION) { 1169 mAncestralToken = tf.readLong(); 1170 mCurrentToken = tf.readLong(); 1171 1172 int numPackages = tf.readInt(); 1173 if (numPackages >= 0) { 1174 mAncestralPackages = new HashSet<String>(); 1175 for (int i = 0; i < numPackages; i++) { 1176 String pkgName = tf.readUTF(); 1177 mAncestralPackages.add(pkgName); 1178 } 1179 } 1180 } 1181 tf.close(); 1182 } catch (FileNotFoundException fnf) { 1183 // Probably innocuous 1184 Slog.v(TAG, "No ancestral data"); 1185 } catch (IOException e) { 1186 Slog.w(TAG, "Unable to read token file", e); 1187 } 1188 1189 // Keep a log of what apps we've ever backed up. Because we might have 1190 // rebooted in the middle of an operation that was removing something from 1191 // this log, we sanity-check its contents here and reconstruct it. 1192 mEverStored = new File(mBaseStateDir, "processed"); 1193 File tempProcessedFile = new File(mBaseStateDir, "processed.new"); 1194 1195 // If we were in the middle of removing something from the ever-backed-up 1196 // file, there might be a transient "processed.new" file still present. 1197 // Ignore it -- we'll validate "processed" against the current package set. 1198 if (tempProcessedFile.exists()) { 1199 tempProcessedFile.delete(); 1200 } 1201 1202 // If there are previous contents, parse them out then start a new 1203 // file to continue the recordkeeping. 1204 if (mEverStored.exists()) { 1205 RandomAccessFile temp = null; 1206 RandomAccessFile in = null; 1207 1208 try { 1209 temp = new RandomAccessFile(tempProcessedFile, "rws"); 1210 in = new RandomAccessFile(mEverStored, "r"); 1211 1212 while (true) { 1213 PackageInfo info; 1214 String pkg = in.readUTF(); 1215 try { 1216 info = mPackageManager.getPackageInfo(pkg, 0); 1217 mEverStoredApps.add(pkg); 1218 temp.writeUTF(pkg); 1219 if (MORE_DEBUG) Slog.v(TAG, " + " + pkg); 1220 } catch (NameNotFoundException e) { 1221 // nope, this package was uninstalled; don't include it 1222 if (MORE_DEBUG) Slog.v(TAG, " - " + pkg); 1223 } 1224 } 1225 } catch (EOFException e) { 1226 // Once we've rewritten the backup history log, atomically replace the 1227 // old one with the new one then reopen the file for continuing use. 1228 if (!tempProcessedFile.renameTo(mEverStored)) { 1229 Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored); 1230 } 1231 } catch (IOException e) { 1232 Slog.e(TAG, "Error in processed file", e); 1233 } finally { 1234 try { if (temp != null) temp.close(); } catch (IOException e) {} 1235 try { if (in != null) in.close(); } catch (IOException e) {} 1236 } 1237 } 1238 1239 // Resume the full-data backup queue 1240 mFullBackupQueue = readFullBackupSchedule(); 1241 1242 // Register for broadcasts about package install, etc., so we can 1243 // update the provider list. 1244 IntentFilter filter = new IntentFilter(); 1245 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 1246 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1247 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1248 filter.addDataScheme("package"); 1249 mContext.registerReceiver(mBroadcastReceiver, filter); 1250 // Register for events related to sdcard installation. 1251 IntentFilter sdFilter = new IntentFilter(); 1252 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 1253 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 1254 mContext.registerReceiver(mBroadcastReceiver, sdFilter); 1255 } 1256 1257 private ArrayList<FullBackupEntry> readFullBackupSchedule() { 1258 ArrayList<FullBackupEntry> schedule = null; 1259 synchronized (mQueueLock) { 1260 if (mFullBackupScheduleFile.exists()) { 1261 FileInputStream fstream = null; 1262 BufferedInputStream bufStream = null; 1263 DataInputStream in = null; 1264 try { 1265 fstream = new FileInputStream(mFullBackupScheduleFile); 1266 bufStream = new BufferedInputStream(fstream); 1267 in = new DataInputStream(bufStream); 1268 1269 int version = in.readInt(); 1270 if (version != SCHEDULE_FILE_VERSION) { 1271 Slog.e(TAG, "Unknown backup schedule version " + version); 1272 return null; 1273 } 1274 1275 int N = in.readInt(); 1276 schedule = new ArrayList<FullBackupEntry>(N); 1277 for (int i = 0; i < N; i++) { 1278 String pkgName = in.readUTF(); 1279 long lastBackup = in.readLong(); 1280 schedule.add(new FullBackupEntry(pkgName, lastBackup)); 1281 } 1282 Collections.sort(schedule); 1283 } catch (Exception e) { 1284 Slog.e(TAG, "Unable to read backup schedule", e); 1285 mFullBackupScheduleFile.delete(); 1286 schedule = null; 1287 } finally { 1288 IoUtils.closeQuietly(in); 1289 IoUtils.closeQuietly(bufStream); 1290 IoUtils.closeQuietly(fstream); 1291 } 1292 } 1293 1294 if (schedule == null) { 1295 // no prior queue record, or unable to read it. Set up the queue 1296 // from scratch. 1297 List<PackageInfo> apps = 1298 PackageManagerBackupAgent.getStorableApplications(mPackageManager); 1299 final int N = apps.size(); 1300 schedule = new ArrayList<FullBackupEntry>(N); 1301 for (int i = 0; i < N; i++) { 1302 PackageInfo info = apps.get(i); 1303 if (appGetsFullBackup(info)) { 1304 schedule.add(new FullBackupEntry(info.packageName, 0)); 1305 } 1306 } 1307 writeFullBackupScheduleAsync(); 1308 } 1309 } 1310 return schedule; 1311 } 1312 1313 Runnable mFullBackupScheduleWriter = new Runnable() { 1314 @Override public void run() { 1315 synchronized (mQueueLock) { 1316 try { 1317 ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096); 1318 DataOutputStream bufOut = new DataOutputStream(bufStream); 1319 bufOut.writeInt(SCHEDULE_FILE_VERSION); 1320 1321 // version 1: 1322 // 1323 // [int] # of packages in the queue = N 1324 // N * { 1325 // [utf8] package name 1326 // [long] last backup time for this package 1327 // } 1328 int N = mFullBackupQueue.size(); 1329 bufOut.writeInt(N); 1330 1331 for (int i = 0; i < N; i++) { 1332 FullBackupEntry entry = mFullBackupQueue.get(i); 1333 bufOut.writeUTF(entry.packageName); 1334 bufOut.writeLong(entry.lastBackup); 1335 } 1336 bufOut.flush(); 1337 1338 AtomicFile af = new AtomicFile(mFullBackupScheduleFile); 1339 FileOutputStream out = af.startWrite(); 1340 out.write(bufStream.toByteArray()); 1341 af.finishWrite(out); 1342 } catch (Exception e) { 1343 Slog.e(TAG, "Unable to write backup schedule!", e); 1344 } 1345 } 1346 } 1347 }; 1348 1349 private void writeFullBackupScheduleAsync() { 1350 mBackupHandler.removeCallbacks(mFullBackupScheduleWriter); 1351 mBackupHandler.post(mFullBackupScheduleWriter); 1352 } 1353 1354 private void parseLeftoverJournals() { 1355 for (File f : mJournalDir.listFiles()) { 1356 if (mJournal == null || f.compareTo(mJournal) != 0) { 1357 // This isn't the current journal, so it must be a leftover. Read 1358 // out the package names mentioned there and schedule them for 1359 // backup. 1360 RandomAccessFile in = null; 1361 try { 1362 Slog.i(TAG, "Found stale backup journal, scheduling"); 1363 in = new RandomAccessFile(f, "r"); 1364 while (true) { 1365 String packageName = in.readUTF(); 1366 if (MORE_DEBUG) Slog.i(TAG, " " + packageName); 1367 dataChangedImpl(packageName); 1368 } 1369 } catch (EOFException e) { 1370 // no more data; we're done 1371 } catch (Exception e) { 1372 Slog.e(TAG, "Can't read " + f, e); 1373 } finally { 1374 // close/delete the file 1375 try { if (in != null) in.close(); } catch (IOException e) {} 1376 f.delete(); 1377 } 1378 } 1379 } 1380 } 1381 1382 private SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) { 1383 return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds); 1384 } 1385 1386 private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) { 1387 try { 1388 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); 1389 KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE); 1390 return keyFactory.generateSecret(ks); 1391 } catch (InvalidKeySpecException e) { 1392 Slog.e(TAG, "Invalid key spec for PBKDF2!"); 1393 } catch (NoSuchAlgorithmException e) { 1394 Slog.e(TAG, "PBKDF2 unavailable!"); 1395 } 1396 return null; 1397 } 1398 1399 private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) { 1400 SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds); 1401 if (key != null) { 1402 return byteArrayToHex(key.getEncoded()); 1403 } 1404 return null; 1405 } 1406 1407 private String byteArrayToHex(byte[] data) { 1408 StringBuilder buf = new StringBuilder(data.length * 2); 1409 for (int i = 0; i < data.length; i++) { 1410 buf.append(Byte.toHexString(data[i], true)); 1411 } 1412 return buf.toString(); 1413 } 1414 1415 private byte[] hexToByteArray(String digits) { 1416 final int bytes = digits.length() / 2; 1417 if (2*bytes != digits.length()) { 1418 throw new IllegalArgumentException("Hex string must have an even number of digits"); 1419 } 1420 1421 byte[] result = new byte[bytes]; 1422 for (int i = 0; i < digits.length(); i += 2) { 1423 result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16); 1424 } 1425 return result; 1426 } 1427 1428 private byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) { 1429 char[] mkAsChar = new char[pwBytes.length]; 1430 for (int i = 0; i < pwBytes.length; i++) { 1431 mkAsChar[i] = (char) pwBytes[i]; 1432 } 1433 1434 Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds); 1435 return checksum.getEncoded(); 1436 } 1437 1438 // Used for generating random salts or passwords 1439 private byte[] randomBytes(int bits) { 1440 byte[] array = new byte[bits / 8]; 1441 mRng.nextBytes(array); 1442 return array; 1443 } 1444 1445 boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) { 1446 if (mPasswordHash == null) { 1447 // no current password case -- require that 'currentPw' be null or empty 1448 if (candidatePw == null || "".equals(candidatePw)) { 1449 return true; 1450 } // else the non-empty candidate does not match the empty stored pw 1451 } else { 1452 // hash the stated current pw and compare to the stored one 1453 if (candidatePw != null && candidatePw.length() > 0) { 1454 String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds); 1455 if (mPasswordHash.equalsIgnoreCase(currentPwHash)) { 1456 // candidate hash matches the stored hash -- the password matches 1457 return true; 1458 } 1459 } // else the stored pw is nonempty but the candidate is empty; no match 1460 } 1461 return false; 1462 } 1463 1464 public boolean setBackupPassword(String currentPw, String newPw) { 1465 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 1466 "setBackupPassword"); 1467 1468 // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes 1469 final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION); 1470 1471 // If the supplied pw doesn't hash to the the saved one, fail. The password 1472 // might be caught in the legacy crypto mismatch; verify that too. 1473 if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) 1474 && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, 1475 currentPw, PBKDF2_HASH_ROUNDS))) { 1476 return false; 1477 } 1478 1479 // Snap up to current on the pw file version 1480 mPasswordVersion = BACKUP_PW_FILE_VERSION; 1481 FileOutputStream pwFout = null; 1482 DataOutputStream pwOut = null; 1483 try { 1484 pwFout = new FileOutputStream(mPasswordVersionFile); 1485 pwOut = new DataOutputStream(pwFout); 1486 pwOut.writeInt(mPasswordVersion); 1487 } catch (IOException e) { 1488 Slog.e(TAG, "Unable to write backup pw version; password not changed"); 1489 return false; 1490 } finally { 1491 try { 1492 if (pwOut != null) pwOut.close(); 1493 if (pwFout != null) pwFout.close(); 1494 } catch (IOException e) { 1495 Slog.w(TAG, "Unable to close pw version record"); 1496 } 1497 } 1498 1499 // Clearing the password is okay 1500 if (newPw == null || newPw.isEmpty()) { 1501 if (mPasswordHashFile.exists()) { 1502 if (!mPasswordHashFile.delete()) { 1503 // Unable to delete the old pw file, so fail 1504 Slog.e(TAG, "Unable to clear backup password"); 1505 return false; 1506 } 1507 } 1508 mPasswordHash = null; 1509 mPasswordSalt = null; 1510 return true; 1511 } 1512 1513 try { 1514 // Okay, build the hash of the new backup password 1515 byte[] salt = randomBytes(PBKDF2_SALT_SIZE); 1516 String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS); 1517 1518 OutputStream pwf = null, buffer = null; 1519 DataOutputStream out = null; 1520 try { 1521 pwf = new FileOutputStream(mPasswordHashFile); 1522 buffer = new BufferedOutputStream(pwf); 1523 out = new DataOutputStream(buffer); 1524 // integer length of the salt array, followed by the salt, 1525 // then the hex pw hash string 1526 out.writeInt(salt.length); 1527 out.write(salt); 1528 out.writeUTF(newPwHash); 1529 out.flush(); 1530 mPasswordHash = newPwHash; 1531 mPasswordSalt = salt; 1532 return true; 1533 } finally { 1534 if (out != null) out.close(); 1535 if (buffer != null) buffer.close(); 1536 if (pwf != null) pwf.close(); 1537 } 1538 } catch (IOException e) { 1539 Slog.e(TAG, "Unable to set backup password"); 1540 } 1541 return false; 1542 } 1543 1544 public boolean hasBackupPassword() { 1545 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 1546 "hasBackupPassword"); 1547 1548 return mPasswordHash != null && mPasswordHash.length() > 0; 1549 } 1550 1551 private boolean backupPasswordMatches(String currentPw) { 1552 if (hasBackupPassword()) { 1553 final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION); 1554 if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) 1555 && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, 1556 currentPw, PBKDF2_HASH_ROUNDS))) { 1557 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 1558 return false; 1559 } 1560 } 1561 return true; 1562 } 1563 1564 // Maintain persistent state around whether need to do an initialize operation. 1565 // Must be called with the queue lock held. 1566 void recordInitPendingLocked(boolean isPending, String transportName) { 1567 if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending 1568 + " on transport " + transportName); 1569 mBackupHandler.removeMessages(MSG_RETRY_INIT); 1570 1571 try { 1572 IBackupTransport transport = getTransport(transportName); 1573 if (transport != null) { 1574 String transportDirName = transport.transportDirName(); 1575 File stateDir = new File(mBaseStateDir, transportDirName); 1576 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME); 1577 1578 if (isPending) { 1579 // We need an init before we can proceed with sending backup data. 1580 // Record that with an entry in our set of pending inits, as well as 1581 // journaling it via creation of a sentinel file. 1582 mPendingInits.add(transportName); 1583 try { 1584 (new FileOutputStream(initPendingFile)).close(); 1585 } catch (IOException ioe) { 1586 // Something is badly wrong with our permissions; just try to move on 1587 } 1588 } else { 1589 // No more initialization needed; wipe the journal and reset our state. 1590 initPendingFile.delete(); 1591 mPendingInits.remove(transportName); 1592 } 1593 return; // done; don't fall through to the error case 1594 } 1595 } catch (RemoteException e) { 1596 // transport threw when asked its name; fall through to the lookup-failed case 1597 } 1598 1599 // The named transport doesn't exist or threw. This operation is 1600 // important, so we record the need for a an init and post a message 1601 // to retry the init later. 1602 if (isPending) { 1603 mPendingInits.add(transportName); 1604 mBackupHandler.sendMessageDelayed( 1605 mBackupHandler.obtainMessage(MSG_RETRY_INIT, 1606 (isPending ? 1 : 0), 1607 0, 1608 transportName), 1609 TRANSPORT_RETRY_INTERVAL); 1610 } 1611 } 1612 1613 // Reset all of our bookkeeping, in response to having been told that 1614 // the backend data has been wiped [due to idle expiry, for example], 1615 // so we must re-upload all saved settings. 1616 void resetBackupState(File stateFileDir) { 1617 synchronized (mQueueLock) { 1618 // Wipe the "what we've ever backed up" tracking 1619 mEverStoredApps.clear(); 1620 mEverStored.delete(); 1621 1622 mCurrentToken = 0; 1623 writeRestoreTokens(); 1624 1625 // Remove all the state files 1626 for (File sf : stateFileDir.listFiles()) { 1627 // ... but don't touch the needs-init sentinel 1628 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) { 1629 sf.delete(); 1630 } 1631 } 1632 } 1633 1634 // Enqueue a new backup of every participant 1635 synchronized (mBackupParticipants) { 1636 final int N = mBackupParticipants.size(); 1637 for (int i=0; i<N; i++) { 1638 HashSet<String> participants = mBackupParticipants.valueAt(i); 1639 if (participants != null) { 1640 for (String packageName : participants) { 1641 dataChangedImpl(packageName); 1642 } 1643 } 1644 } 1645 } 1646 } 1647 1648 // Add a transport to our set of available backends. If 'transport' is null, this 1649 // is an unregistration, and the transport's entry is removed from our bookkeeping. 1650 private void registerTransport(String name, String component, IBackupTransport transport) { 1651 synchronized (mTransports) { 1652 if (DEBUG) Slog.v(TAG, "Registering transport " 1653 + component + "::" + name + " = " + transport); 1654 if (transport != null) { 1655 mTransports.put(name, transport); 1656 mTransportNames.put(component, name); 1657 } else { 1658 mTransports.remove(mTransportNames.get(component)); 1659 mTransportNames.remove(component); 1660 // Nothing further to do in the unregistration case 1661 return; 1662 } 1663 } 1664 1665 // If the init sentinel file exists, we need to be sure to perform the init 1666 // as soon as practical. We also create the state directory at registration 1667 // time to ensure it's present from the outset. 1668 try { 1669 String transportName = transport.transportDirName(); 1670 File stateDir = new File(mBaseStateDir, transportName); 1671 stateDir.mkdirs(); 1672 1673 File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME); 1674 if (initSentinel.exists()) { 1675 synchronized (mQueueLock) { 1676 mPendingInits.add(transportName); 1677 1678 // TODO: pick a better starting time than now + 1 minute 1679 long delay = 1000 * 60; // one minute, in milliseconds 1680 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 1681 System.currentTimeMillis() + delay, mRunInitIntent); 1682 } 1683 } 1684 } catch (RemoteException e) { 1685 // the transport threw when asked its file naming prefs; declare it invalid 1686 Slog.e(TAG, "Unable to register transport as " + name); 1687 mTransportNames.remove(component); 1688 mTransports.remove(name); 1689 } 1690 } 1691 1692 // ----- Track installation/removal of packages ----- 1693 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1694 public void onReceive(Context context, Intent intent) { 1695 if (DEBUG) Slog.d(TAG, "Received broadcast " + intent); 1696 1697 String action = intent.getAction(); 1698 boolean replacing = false; 1699 boolean added = false; 1700 boolean changed = false; 1701 Bundle extras = intent.getExtras(); 1702 String pkgList[] = null; 1703 if (Intent.ACTION_PACKAGE_ADDED.equals(action) || 1704 Intent.ACTION_PACKAGE_REMOVED.equals(action) || 1705 Intent.ACTION_PACKAGE_CHANGED.equals(action)) { 1706 Uri uri = intent.getData(); 1707 if (uri == null) { 1708 return; 1709 } 1710 String pkgName = uri.getSchemeSpecificPart(); 1711 if (pkgName != null) { 1712 pkgList = new String[] { pkgName }; 1713 } 1714 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action); 1715 1716 // At package-changed we only care about looking at new transport states 1717 if (changed) { 1718 try { 1719 if (MORE_DEBUG) { 1720 Slog.i(TAG, "Package " + pkgName + " changed; rechecking"); 1721 } 1722 // unbind existing possibly-stale connections to that package's transports 1723 synchronized (mTransports) { 1724 TransportConnection conn = mTransportConnections.get(pkgName); 1725 if (conn != null) { 1726 final ServiceInfo svc = conn.mTransport; 1727 ComponentName svcName = 1728 new ComponentName(svc.packageName, svc.name); 1729 String flatName = svcName.flattenToShortString(); 1730 Slog.i(TAG, "Unbinding " + svcName); 1731 1732 mContext.unbindService(conn); 1733 mTransportConnections.remove(pkgName); 1734 mTransports.remove(mTransportNames.get(flatName)); 1735 mTransportNames.remove(flatName); 1736 } 1737 } 1738 // and then (re)bind as appropriate 1739 PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0); 1740 checkForTransportAndBind(app); 1741 } catch (NameNotFoundException e) { 1742 // Nope, can't find it - just ignore 1743 if (MORE_DEBUG) { 1744 Slog.w(TAG, "Can't find changed package " + pkgName); 1745 } 1746 } 1747 return; // nothing more to do in the PACKAGE_CHANGED case 1748 } 1749 1750 added = Intent.ACTION_PACKAGE_ADDED.equals(action); 1751 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); 1752 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { 1753 added = true; 1754 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1755 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { 1756 added = false; 1757 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1758 } 1759 1760 if (pkgList == null || pkgList.length == 0) { 1761 return; 1762 } 1763 1764 final int uid = extras.getInt(Intent.EXTRA_UID); 1765 if (added) { 1766 synchronized (mBackupParticipants) { 1767 if (replacing) { 1768 // This is the package-replaced case; we just remove the entry 1769 // under the old uid and fall through to re-add. 1770 removePackageParticipantsLocked(pkgList, uid); 1771 } 1772 addPackageParticipantsLocked(pkgList); 1773 } 1774 // If they're full-backup candidates, add them there instead 1775 for (String packageName : pkgList) { 1776 try { 1777 PackageInfo app = mPackageManager.getPackageInfo(packageName, 0); 1778 long now = System.currentTimeMillis(); 1779 if (appGetsFullBackup(app)) { 1780 enqueueFullBackup(packageName, now); 1781 scheduleNextFullBackupJob(); 1782 } 1783 1784 // Transport maintenance: rebind to known existing transports that have 1785 // just been updated; and bind to any newly-installed transport services. 1786 synchronized (mTransports) { 1787 final TransportConnection conn = mTransportConnections.get(packageName); 1788 if (conn != null) { 1789 if (MORE_DEBUG) { 1790 Slog.i(TAG, "Transport package changed; rebinding"); 1791 } 1792 bindTransport(conn.mTransport); 1793 } else { 1794 checkForTransportAndBind(app); 1795 } 1796 } 1797 1798 } catch (NameNotFoundException e) { 1799 // doesn't really exist; ignore it 1800 if (DEBUG) { 1801 Slog.i(TAG, "Can't resolve new app " + packageName); 1802 } 1803 } 1804 } 1805 1806 } else { 1807 if (replacing) { 1808 // The package is being updated. We'll receive a PACKAGE_ADDED shortly. 1809 } else { 1810 synchronized (mBackupParticipants) { 1811 removePackageParticipantsLocked(pkgList, uid); 1812 } 1813 } 1814 } 1815 } 1816 }; 1817 1818 // ----- Track connection to transports service ----- 1819 class TransportConnection implements ServiceConnection { 1820 ServiceInfo mTransport; 1821 1822 public TransportConnection(ServiceInfo transport) { 1823 mTransport = transport; 1824 } 1825 1826 @Override 1827 public void onServiceConnected(ComponentName component, IBinder service) { 1828 if (DEBUG) Slog.v(TAG, "Connected to transport " + component); 1829 final String name = component.flattenToShortString(); 1830 try { 1831 IBackupTransport transport = IBackupTransport.Stub.asInterface(service); 1832 registerTransport(transport.name(), name, transport); 1833 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 1); 1834 } catch (RemoteException e) { 1835 Slog.e(TAG, "Unable to register transport " + component); 1836 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0); 1837 } 1838 } 1839 1840 @Override 1841 public void onServiceDisconnected(ComponentName component) { 1842 if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component); 1843 final String name = component.flattenToShortString(); 1844 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0); 1845 registerTransport(null, name, null); 1846 } 1847 }; 1848 1849 // Check whether the given package hosts a transport, and bind if so 1850 void checkForTransportAndBind(PackageInfo pkgInfo) { 1851 Intent intent = new Intent(mTransportServiceIntent) 1852 .setPackage(pkgInfo.packageName); 1853 List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser( 1854 intent, 0, UserHandle.USER_OWNER); 1855 final int N = hosts.size(); 1856 for (int i = 0; i < N; i++) { 1857 final ServiceInfo info = hosts.get(i).serviceInfo; 1858 tryBindTransport(info); 1859 } 1860 } 1861 1862 // Verify that the service exists and is hosted by a privileged app, then proceed to bind 1863 boolean tryBindTransport(ServiceInfo info) { 1864 try { 1865 PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0); 1866 if ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) { 1867 return bindTransport(info); 1868 } else { 1869 Slog.w(TAG, "Transport package " + info.packageName + " not privileged"); 1870 } 1871 } catch (NameNotFoundException e) { 1872 Slog.w(TAG, "Problem resolving transport package " + info.packageName); 1873 } 1874 return false; 1875 } 1876 1877 // Actually bind; presumes that we have already validated the transport service 1878 boolean bindTransport(ServiceInfo transport) { 1879 ComponentName svcName = new ComponentName(transport.packageName, transport.name); 1880 if (DEBUG) { 1881 Slog.i(TAG, "Binding to transport host " + svcName); 1882 } 1883 Intent intent = new Intent(mTransportServiceIntent); 1884 intent.setComponent(svcName); 1885 1886 TransportConnection connection; 1887 synchronized (mTransports) { 1888 connection = mTransportConnections.get(transport.packageName); 1889 if (null == connection) { 1890 connection = new TransportConnection(transport); 1891 mTransportConnections.put(transport.packageName, connection); 1892 } else { 1893 // This is a rebind due to package upgrade. The service won't be 1894 // automatically relaunched for us until we explicitly rebind, but 1895 // we need to unbind the now-orphaned original connection. 1896 mContext.unbindService(connection); 1897 } 1898 } 1899 return mContext.bindServiceAsUser(intent, 1900 connection, Context.BIND_AUTO_CREATE, 1901 UserHandle.OWNER); 1902 } 1903 1904 // Add the backup agents in the given packages to our set of known backup participants. 1905 // If 'packageNames' is null, adds all backup agents in the whole system. 1906 void addPackageParticipantsLocked(String[] packageNames) { 1907 // Look for apps that define the android:backupAgent attribute 1908 List<PackageInfo> targetApps = allAgentPackages(); 1909 if (packageNames != null) { 1910 if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length); 1911 for (String packageName : packageNames) { 1912 addPackageParticipantsLockedInner(packageName, targetApps); 1913 } 1914 } else { 1915 if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all"); 1916 addPackageParticipantsLockedInner(null, targetApps); 1917 } 1918 } 1919 1920 private void addPackageParticipantsLockedInner(String packageName, 1921 List<PackageInfo> targetPkgs) { 1922 if (MORE_DEBUG) { 1923 Slog.v(TAG, "Examining " + packageName + " for backup agent"); 1924 } 1925 1926 for (PackageInfo pkg : targetPkgs) { 1927 if (packageName == null || pkg.packageName.equals(packageName)) { 1928 int uid = pkg.applicationInfo.uid; 1929 HashSet<String> set = mBackupParticipants.get(uid); 1930 if (set == null) { 1931 set = new HashSet<String>(); 1932 mBackupParticipants.put(uid, set); 1933 } 1934 set.add(pkg.packageName); 1935 if (MORE_DEBUG) Slog.v(TAG, "Agent found; added"); 1936 1937 // Schedule a backup for it on general principles 1938 if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName); 1939 dataChangedImpl(pkg.packageName); 1940 } 1941 } 1942 } 1943 1944 // Remove the given packages' entries from our known active set. 1945 void removePackageParticipantsLocked(String[] packageNames, int oldUid) { 1946 if (packageNames == null) { 1947 Slog.w(TAG, "removePackageParticipants with null list"); 1948 return; 1949 } 1950 1951 if (MORE_DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid 1952 + " #" + packageNames.length); 1953 for (String pkg : packageNames) { 1954 // Known previous UID, so we know which package set to check 1955 HashSet<String> set = mBackupParticipants.get(oldUid); 1956 if (set != null && set.contains(pkg)) { 1957 removePackageFromSetLocked(set, pkg); 1958 if (set.isEmpty()) { 1959 if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set"); 1960 mBackupParticipants.remove(oldUid); 1961 } 1962 } 1963 } 1964 } 1965 1966 private void removePackageFromSetLocked(final HashSet<String> set, 1967 final String packageName) { 1968 if (set.contains(packageName)) { 1969 // Found it. Remove this one package from the bookkeeping, and 1970 // if it's the last participating app under this uid we drop the 1971 // (now-empty) set as well. 1972 // Note that we deliberately leave it 'known' in the "ever backed up" 1973 // bookkeeping so that its current-dataset data will be retrieved 1974 // if the app is subsequently reinstalled 1975 if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName); 1976 set.remove(packageName); 1977 mPendingBackups.remove(packageName); 1978 } 1979 } 1980 1981 // Returns the set of all applications that define an android:backupAgent attribute 1982 List<PackageInfo> allAgentPackages() { 1983 // !!! TODO: cache this and regenerate only when necessary 1984 int flags = PackageManager.GET_SIGNATURES; 1985 List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags); 1986 int N = packages.size(); 1987 for (int a = N-1; a >= 0; a--) { 1988 PackageInfo pkg = packages.get(a); 1989 try { 1990 ApplicationInfo app = pkg.applicationInfo; 1991 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) 1992 || app.backupAgentName == null) { 1993 packages.remove(a); 1994 } 1995 else { 1996 // we will need the shared library path, so look that up and store it here. 1997 // This is used implicitly when we pass the PackageInfo object off to 1998 // the Activity Manager to launch the app for backup/restore purposes. 1999 app = mPackageManager.getApplicationInfo(pkg.packageName, 2000 PackageManager.GET_SHARED_LIBRARY_FILES); 2001 pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles; 2002 } 2003 } catch (NameNotFoundException e) { 2004 packages.remove(a); 2005 } 2006 } 2007 return packages; 2008 } 2009 2010 // Called from the backup tasks: record that the given app has been successfully 2011 // backed up at least once. This includes both key/value and full-data backups 2012 // through the transport. 2013 void logBackupComplete(String packageName) { 2014 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return; 2015 2016 synchronized (mEverStoredApps) { 2017 if (!mEverStoredApps.add(packageName)) return; 2018 2019 RandomAccessFile out = null; 2020 try { 2021 out = new RandomAccessFile(mEverStored, "rws"); 2022 out.seek(out.length()); 2023 out.writeUTF(packageName); 2024 } catch (IOException e) { 2025 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored); 2026 } finally { 2027 try { if (out != null) out.close(); } catch (IOException e) {} 2028 } 2029 } 2030 } 2031 2032 // Remove our awareness of having ever backed up the given package 2033 void removeEverBackedUp(String packageName) { 2034 if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName); 2035 if (MORE_DEBUG) Slog.v(TAG, "New set:"); 2036 2037 synchronized (mEverStoredApps) { 2038 // Rewrite the file and rename to overwrite. If we reboot in the middle, 2039 // we'll recognize on initialization time that the package no longer 2040 // exists and fix it up then. 2041 File tempKnownFile = new File(mBaseStateDir, "processed.new"); 2042 RandomAccessFile known = null; 2043 try { 2044 known = new RandomAccessFile(tempKnownFile, "rws"); 2045 mEverStoredApps.remove(packageName); 2046 for (String s : mEverStoredApps) { 2047 known.writeUTF(s); 2048 if (MORE_DEBUG) Slog.v(TAG, " " + s); 2049 } 2050 known.close(); 2051 known = null; 2052 if (!tempKnownFile.renameTo(mEverStored)) { 2053 throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored); 2054 } 2055 } catch (IOException e) { 2056 // Bad: we couldn't create the new copy. For safety's sake we 2057 // abandon the whole process and remove all what's-backed-up 2058 // state entirely, meaning we'll force a backup pass for every 2059 // participant on the next boot or [re]install. 2060 Slog.w(TAG, "Error rewriting " + mEverStored, e); 2061 mEverStoredApps.clear(); 2062 tempKnownFile.delete(); 2063 mEverStored.delete(); 2064 } finally { 2065 try { if (known != null) known.close(); } catch (IOException e) {} 2066 } 2067 } 2068 } 2069 2070 // Persistently record the current and ancestral backup tokens as well 2071 // as the set of packages with data [supposedly] available in the 2072 // ancestral dataset. 2073 void writeRestoreTokens() { 2074 try { 2075 RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd"); 2076 2077 // First, the version number of this record, for futureproofing 2078 af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION); 2079 2080 // Write the ancestral and current tokens 2081 af.writeLong(mAncestralToken); 2082 af.writeLong(mCurrentToken); 2083 2084 // Now write the set of ancestral packages 2085 if (mAncestralPackages == null) { 2086 af.writeInt(-1); 2087 } else { 2088 af.writeInt(mAncestralPackages.size()); 2089 if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size()); 2090 for (String pkgName : mAncestralPackages) { 2091 af.writeUTF(pkgName); 2092 if (MORE_DEBUG) Slog.v(TAG, " " + pkgName); 2093 } 2094 } 2095 af.close(); 2096 } catch (IOException e) { 2097 Slog.w(TAG, "Unable to write token file:", e); 2098 } 2099 } 2100 2101 // Return the given transport 2102 private IBackupTransport getTransport(String transportName) { 2103 synchronized (mTransports) { 2104 IBackupTransport transport = mTransports.get(transportName); 2105 if (transport == null) { 2106 Slog.w(TAG, "Requested unavailable transport: " + transportName); 2107 } 2108 return transport; 2109 } 2110 } 2111 2112 // fire off a backup agent, blocking until it attaches or times out 2113 IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) { 2114 IBackupAgent agent = null; 2115 synchronized(mAgentConnectLock) { 2116 mConnecting = true; 2117 mConnectedAgent = null; 2118 try { 2119 if (mActivityManager.bindBackupAgent(app, mode)) { 2120 Slog.d(TAG, "awaiting agent for " + app); 2121 2122 // success; wait for the agent to arrive 2123 // only wait 10 seconds for the bind to happen 2124 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; 2125 while (mConnecting && mConnectedAgent == null 2126 && (System.currentTimeMillis() < timeoutMark)) { 2127 try { 2128 mAgentConnectLock.wait(5000); 2129 } catch (InterruptedException e) { 2130 // just bail 2131 if (DEBUG) Slog.w(TAG, "Interrupted: " + e); 2132 mActivityManager.clearPendingBackup(); 2133 return null; 2134 } 2135 } 2136 2137 // if we timed out with no connect, abort and move on 2138 if (mConnecting == true) { 2139 Slog.w(TAG, "Timeout waiting for agent " + app); 2140 mActivityManager.clearPendingBackup(); 2141 return null; 2142 } 2143 if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent); 2144 agent = mConnectedAgent; 2145 } 2146 } catch (RemoteException e) { 2147 // can't happen - ActivityManager is local 2148 } 2149 } 2150 return agent; 2151 } 2152 2153 // clear an application's data, blocking until the operation completes or times out 2154 void clearApplicationDataSynchronous(String packageName) { 2155 // Don't wipe packages marked allowClearUserData=false 2156 try { 2157 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); 2158 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) { 2159 if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping " 2160 + packageName); 2161 return; 2162 } 2163 } catch (NameNotFoundException e) { 2164 Slog.w(TAG, "Tried to clear data for " + packageName + " but not found"); 2165 return; 2166 } 2167 2168 ClearDataObserver observer = new ClearDataObserver(); 2169 2170 synchronized(mClearDataLock) { 2171 mClearingData = true; 2172 try { 2173 mActivityManager.clearApplicationUserData(packageName, observer, 0); 2174 } catch (RemoteException e) { 2175 // can't happen because the activity manager is in this process 2176 } 2177 2178 // only wait 10 seconds for the clear data to happen 2179 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; 2180 while (mClearingData && (System.currentTimeMillis() < timeoutMark)) { 2181 try { 2182 mClearDataLock.wait(5000); 2183 } catch (InterruptedException e) { 2184 // won't happen, but still. 2185 mClearingData = false; 2186 } 2187 } 2188 } 2189 } 2190 2191 class ClearDataObserver extends IPackageDataObserver.Stub { 2192 public void onRemoveCompleted(String packageName, boolean succeeded) { 2193 synchronized(mClearDataLock) { 2194 mClearingData = false; 2195 mClearDataLock.notifyAll(); 2196 } 2197 } 2198 } 2199 2200 // Get the restore-set token for the best-available restore set for this package: 2201 // the active set if possible, else the ancestral one. Returns zero if none available. 2202 long getAvailableRestoreToken(String packageName) { 2203 long token = mAncestralToken; 2204 synchronized (mQueueLock) { 2205 if (mEverStoredApps.contains(packageName)) { 2206 token = mCurrentToken; 2207 } 2208 } 2209 return token; 2210 } 2211 2212 // ----- 2213 // Interface and methods used by the asynchronous-with-timeout backup/restore operations 2214 2215 interface BackupRestoreTask { 2216 // Execute one tick of whatever state machine the task implements 2217 void execute(); 2218 2219 // An operation that wanted a callback has completed 2220 void operationComplete(); 2221 2222 // An operation that wanted a callback has timed out 2223 void handleTimeout(); 2224 } 2225 2226 void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) { 2227 if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token) 2228 + " interval=" + interval); 2229 synchronized (mCurrentOpLock) { 2230 mCurrentOperations.put(token, new Operation(OP_PENDING, callback)); 2231 2232 Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback); 2233 mBackupHandler.sendMessageDelayed(msg, interval); 2234 } 2235 } 2236 2237 // synchronous waiter case 2238 boolean waitUntilOperationComplete(int token) { 2239 if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for " 2240 + Integer.toHexString(token)); 2241 int finalState = OP_PENDING; 2242 Operation op = null; 2243 synchronized (mCurrentOpLock) { 2244 while (true) { 2245 op = mCurrentOperations.get(token); 2246 if (op == null) { 2247 // mysterious disappearance: treat as success with no callback 2248 break; 2249 } else { 2250 if (op.state == OP_PENDING) { 2251 try { 2252 mCurrentOpLock.wait(); 2253 } catch (InterruptedException e) {} 2254 // When the wait is notified we loop around and recheck the current state 2255 } else { 2256 // No longer pending; we're done 2257 finalState = op.state; 2258 break; 2259 } 2260 } 2261 } 2262 } 2263 2264 mBackupHandler.removeMessages(MSG_TIMEOUT); 2265 if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token) 2266 + " complete: finalState=" + finalState); 2267 return finalState == OP_ACKNOWLEDGED; 2268 } 2269 2270 void handleTimeout(int token, Object obj) { 2271 // Notify any synchronous waiters 2272 Operation op = null; 2273 synchronized (mCurrentOpLock) { 2274 op = mCurrentOperations.get(token); 2275 if (MORE_DEBUG) { 2276 if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token) 2277 + " but no op found"); 2278 } 2279 int state = (op != null) ? op.state : OP_TIMEOUT; 2280 if (state == OP_PENDING) { 2281 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token)); 2282 op.state = OP_TIMEOUT; 2283 mCurrentOperations.put(token, op); 2284 } 2285 mCurrentOpLock.notifyAll(); 2286 } 2287 2288 // If there's a TimeoutHandler for this event, call it 2289 if (op != null && op.callback != null) { 2290 op.callback.handleTimeout(); 2291 } 2292 } 2293 2294 // ----- Back up a set of applications via a worker thread ----- 2295 2296 enum BackupState { 2297 INITIAL, 2298 RUNNING_QUEUE, 2299 FINAL 2300 } 2301 2302 class PerformBackupTask implements BackupRestoreTask { 2303 private static final String TAG = "PerformBackupTask"; 2304 2305 IBackupTransport mTransport; 2306 ArrayList<BackupRequest> mQueue; 2307 ArrayList<BackupRequest> mOriginalQueue; 2308 File mStateDir; 2309 File mJournal; 2310 BackupState mCurrentState; 2311 2312 // carried information about the current in-flight operation 2313 IBackupAgent mAgentBinder; 2314 PackageInfo mCurrentPackage; 2315 File mSavedStateName; 2316 File mBackupDataName; 2317 File mNewStateName; 2318 ParcelFileDescriptor mSavedState; 2319 ParcelFileDescriptor mBackupData; 2320 ParcelFileDescriptor mNewState; 2321 int mStatus; 2322 boolean mFinished; 2323 2324 public PerformBackupTask(IBackupTransport transport, String dirName, 2325 ArrayList<BackupRequest> queue, File journal) { 2326 mTransport = transport; 2327 mOriginalQueue = queue; 2328 mJournal = journal; 2329 2330 mStateDir = new File(mBaseStateDir, dirName); 2331 2332 mCurrentState = BackupState.INITIAL; 2333 mFinished = false; 2334 2335 addBackupTrace("STATE => INITIAL"); 2336 } 2337 2338 // Main entry point: perform one chunk of work, updating the state as appropriate 2339 // and reposting the next chunk to the primary backup handler thread. 2340 @Override 2341 public void execute() { 2342 switch (mCurrentState) { 2343 case INITIAL: 2344 beginBackup(); 2345 break; 2346 2347 case RUNNING_QUEUE: 2348 invokeNextAgent(); 2349 break; 2350 2351 case FINAL: 2352 if (!mFinished) finalizeBackup(); 2353 else { 2354 Slog.e(TAG, "Duplicate finish"); 2355 } 2356 mFinished = true; 2357 break; 2358 } 2359 } 2360 2361 // We're starting a backup pass. Initialize the transport and send 2362 // the PM metadata blob if we haven't already. 2363 void beginBackup() { 2364 if (DEBUG_BACKUP_TRACE) { 2365 clearBackupTrace(); 2366 StringBuilder b = new StringBuilder(256); 2367 b.append("beginBackup: ["); 2368 for (BackupRequest req : mOriginalQueue) { 2369 b.append(' '); 2370 b.append(req.packageName); 2371 } 2372 b.append(" ]"); 2373 addBackupTrace(b.toString()); 2374 } 2375 2376 mAgentBinder = null; 2377 mStatus = BackupTransport.TRANSPORT_OK; 2378 2379 // Sanity check: if the queue is empty we have no work to do. 2380 if (mOriginalQueue.isEmpty()) { 2381 Slog.w(TAG, "Backup begun with an empty queue - nothing to do."); 2382 addBackupTrace("queue empty at begin"); 2383 executeNextState(BackupState.FINAL); 2384 return; 2385 } 2386 2387 // We need to retain the original queue contents in case of transport 2388 // failure, but we want a working copy that we can manipulate along 2389 // the way. 2390 mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone(); 2391 2392 if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); 2393 2394 File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); 2395 try { 2396 final String transportName = mTransport.transportDirName(); 2397 EventLog.writeEvent(EventLogTags.BACKUP_START, transportName); 2398 2399 // If we haven't stored package manager metadata yet, we must init the transport. 2400 if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) { 2401 Slog.i(TAG, "Initializing (wiping) backup state and transport storage"); 2402 addBackupTrace("initializing transport " + transportName); 2403 resetBackupState(mStateDir); // Just to make sure. 2404 mStatus = mTransport.initializeDevice(); 2405 2406 addBackupTrace("transport.initializeDevice() == " + mStatus); 2407 if (mStatus == BackupTransport.TRANSPORT_OK) { 2408 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 2409 } else { 2410 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 2411 Slog.e(TAG, "Transport error in initializeDevice()"); 2412 } 2413 } 2414 2415 // The package manager doesn't have a proper <application> etc, but since 2416 // it's running here in the system process we can just set up its agent 2417 // directly and use a synthetic BackupRequest. We always run this pass 2418 // because it's cheap and this way we guarantee that we don't get out of 2419 // step even if we're selecting among various transports at run time. 2420 if (mStatus == BackupTransport.TRANSPORT_OK) { 2421 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( 2422 mPackageManager); 2423 mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL, 2424 IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); 2425 addBackupTrace("PMBA invoke: " + mStatus); 2426 2427 // Because the PMBA is a local instance, it has already executed its 2428 // backup callback and returned. Blow away the lingering (spurious) 2429 // pending timeout message for it. 2430 mBackupHandler.removeMessages(MSG_TIMEOUT); 2431 } 2432 2433 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { 2434 // The backend reports that our dataset has been wiped. Note this in 2435 // the event log; the no-success code below will reset the backup 2436 // state as well. 2437 EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName()); 2438 } 2439 } catch (Exception e) { 2440 Slog.e(TAG, "Error in backup thread", e); 2441 addBackupTrace("Exception in backup thread: " + e); 2442 mStatus = BackupTransport.TRANSPORT_ERROR; 2443 } finally { 2444 // If we've succeeded so far, invokeAgentForBackup() will have run the PM 2445 // metadata and its completion/timeout callback will continue the state 2446 // machine chain. If it failed that won't happen; we handle that now. 2447 addBackupTrace("exiting prelim: " + mStatus); 2448 if (mStatus != BackupTransport.TRANSPORT_OK) { 2449 // if things went wrong at this point, we need to 2450 // restage everything and try again later. 2451 resetBackupState(mStateDir); // Just to make sure. 2452 executeNextState(BackupState.FINAL); 2453 } 2454 } 2455 } 2456 2457 // Transport has been initialized and the PM metadata submitted successfully 2458 // if that was warranted. Now we process the single next thing in the queue. 2459 void invokeNextAgent() { 2460 mStatus = BackupTransport.TRANSPORT_OK; 2461 addBackupTrace("invoke q=" + mQueue.size()); 2462 2463 // Sanity check that we have work to do. If not, skip to the end where 2464 // we reestablish the wakelock invariants etc. 2465 if (mQueue.isEmpty()) { 2466 if (DEBUG) Slog.i(TAG, "queue now empty"); 2467 executeNextState(BackupState.FINAL); 2468 return; 2469 } 2470 2471 // pop the entry we're going to process on this step 2472 BackupRequest request = mQueue.get(0); 2473 mQueue.remove(0); 2474 2475 Slog.d(TAG, "starting agent for backup of " + request); 2476 addBackupTrace("launch agent for " + request.packageName); 2477 2478 // Verify that the requested app exists; it might be something that 2479 // requested a backup but was then uninstalled. The request was 2480 // journalled and rather than tamper with the journal it's safer 2481 // to sanity-check here. This also gives us the classname of the 2482 // package's backup agent. 2483 try { 2484 mCurrentPackage = mPackageManager.getPackageInfo(request.packageName, 2485 PackageManager.GET_SIGNATURES); 2486 if (mCurrentPackage.applicationInfo.backupAgentName == null) { 2487 // The manifest has changed but we had a stale backup request pending. 2488 // This won't happen again because the app won't be requesting further 2489 // backups. 2490 Slog.i(TAG, "Package " + request.packageName 2491 + " no longer supports backup; skipping"); 2492 addBackupTrace("skipping - no agent, completion is noop"); 2493 executeNextState(BackupState.RUNNING_QUEUE); 2494 return; 2495 } 2496 2497 if ((mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) { 2498 // The app has been force-stopped or cleared or just installed, 2499 // and not yet launched out of that state, so just as it won't 2500 // receive broadcasts, we won't run it for backup. 2501 addBackupTrace("skipping - stopped"); 2502 executeNextState(BackupState.RUNNING_QUEUE); 2503 return; 2504 } 2505 2506 IBackupAgent agent = null; 2507 try { 2508 mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid)); 2509 agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo, 2510 IApplicationThread.BACKUP_MODE_INCREMENTAL); 2511 addBackupTrace("agent bound; a? = " + (agent != null)); 2512 if (agent != null) { 2513 mAgentBinder = agent; 2514 mStatus = invokeAgentForBackup(request.packageName, agent, mTransport); 2515 // at this point we'll either get a completion callback from the 2516 // agent, or a timeout message on the main handler. either way, we're 2517 // done here as long as we're successful so far. 2518 } else { 2519 // Timeout waiting for the agent 2520 mStatus = BackupTransport.AGENT_ERROR; 2521 } 2522 } catch (SecurityException ex) { 2523 // Try for the next one. 2524 Slog.d(TAG, "error in bind/backup", ex); 2525 mStatus = BackupTransport.AGENT_ERROR; 2526 addBackupTrace("agent SE"); 2527 } 2528 } catch (NameNotFoundException e) { 2529 Slog.d(TAG, "Package does not exist; skipping"); 2530 addBackupTrace("no such package"); 2531 mStatus = BackupTransport.AGENT_UNKNOWN; 2532 } finally { 2533 mWakelock.setWorkSource(null); 2534 2535 // If there was an agent error, no timeout/completion handling will occur. 2536 // That means we need to direct to the next state ourselves. 2537 if (mStatus != BackupTransport.TRANSPORT_OK) { 2538 BackupState nextState = BackupState.RUNNING_QUEUE; 2539 mAgentBinder = null; 2540 2541 // An agent-level failure means we reenqueue this one agent for 2542 // a later retry, but otherwise proceed normally. 2543 if (mStatus == BackupTransport.AGENT_ERROR) { 2544 if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName 2545 + " - restaging"); 2546 dataChangedImpl(request.packageName); 2547 mStatus = BackupTransport.TRANSPORT_OK; 2548 if (mQueue.isEmpty()) nextState = BackupState.FINAL; 2549 } else if (mStatus == BackupTransport.AGENT_UNKNOWN) { 2550 // Failed lookup of the app, so we couldn't bring up an agent, but 2551 // we're otherwise fine. Just drop it and go on to the next as usual. 2552 mStatus = BackupTransport.TRANSPORT_OK; 2553 } else { 2554 // Transport-level failure means we reenqueue everything 2555 revertAndEndBackup(); 2556 nextState = BackupState.FINAL; 2557 } 2558 2559 executeNextState(nextState); 2560 } else { 2561 // success case 2562 addBackupTrace("expecting completion/timeout callback"); 2563 } 2564 } 2565 } 2566 2567 void finalizeBackup() { 2568 addBackupTrace("finishing"); 2569 2570 // Either backup was successful, in which case we of course do not need 2571 // this pass's journal any more; or it failed, in which case we just 2572 // re-enqueued all of these packages in the current active journal. 2573 // Either way, we no longer need this pass's journal. 2574 if (mJournal != null && !mJournal.delete()) { 2575 Slog.e(TAG, "Unable to remove backup journal file " + mJournal); 2576 } 2577 2578 // If everything actually went through and this is the first time we've 2579 // done a backup, we can now record what the current backup dataset token 2580 // is. 2581 if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) { 2582 addBackupTrace("success; recording token"); 2583 try { 2584 mCurrentToken = mTransport.getCurrentRestoreSet(); 2585 writeRestoreTokens(); 2586 } catch (RemoteException e) { 2587 // nothing for it at this point, unfortunately, but this will be 2588 // recorded the next time we fully succeed. 2589 addBackupTrace("transport threw returning token"); 2590 } 2591 } 2592 2593 // Set up the next backup pass - at this point we can set mBackupRunning 2594 // to false to allow another pass to fire, because we're done with the 2595 // state machine sequence and the wakelock is refcounted. 2596 synchronized (mQueueLock) { 2597 mBackupRunning = false; 2598 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { 2599 // Make sure we back up everything and perform the one-time init 2600 clearMetadata(); 2601 if (DEBUG) Slog.d(TAG, "Server requires init; rerunning"); 2602 addBackupTrace("init required; rerunning"); 2603 backupNow(); 2604 } 2605 } 2606 2607 // Only once we're entirely finished do we release the wakelock 2608 clearBackupTrace(); 2609 Slog.i(BackupManagerService.TAG, "Backup pass finished."); 2610 mWakelock.release(); 2611 } 2612 2613 // Remove the PM metadata state. This will generate an init on the next pass. 2614 void clearMetadata() { 2615 final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); 2616 if (pmState.exists()) pmState.delete(); 2617 } 2618 2619 // Invoke an agent's doBackup() and start a timeout message spinning on the main 2620 // handler in case it doesn't get back to us. 2621 int invokeAgentForBackup(String packageName, IBackupAgent agent, 2622 IBackupTransport transport) { 2623 if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName); 2624 addBackupTrace("invoking " + packageName); 2625 2626 mSavedStateName = new File(mStateDir, packageName); 2627 mBackupDataName = new File(mDataDir, packageName + ".data"); 2628 mNewStateName = new File(mStateDir, packageName + ".new"); 2629 if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName); 2630 2631 mSavedState = null; 2632 mBackupData = null; 2633 mNewState = null; 2634 2635 final int token = generateToken(); 2636 try { 2637 // Look up the package info & signatures. This is first so that if it 2638 // throws an exception, there's no file setup yet that would need to 2639 // be unraveled. 2640 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) { 2641 // The metadata 'package' is synthetic; construct one and make 2642 // sure our global state is pointed at it 2643 mCurrentPackage = new PackageInfo(); 2644 mCurrentPackage.packageName = packageName; 2645 } 2646 2647 // In a full backup, we pass a null ParcelFileDescriptor as 2648 // the saved-state "file". This is by definition an incremental, 2649 // so we build a saved state file to pass. 2650 mSavedState = ParcelFileDescriptor.open(mSavedStateName, 2651 ParcelFileDescriptor.MODE_READ_ONLY | 2652 ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary 2653 2654 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 2655 ParcelFileDescriptor.MODE_READ_WRITE | 2656 ParcelFileDescriptor.MODE_CREATE | 2657 ParcelFileDescriptor.MODE_TRUNCATE); 2658 2659 if (!SELinux.restorecon(mBackupDataName)) { 2660 Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName); 2661 } 2662 2663 mNewState = ParcelFileDescriptor.open(mNewStateName, 2664 ParcelFileDescriptor.MODE_READ_WRITE | 2665 ParcelFileDescriptor.MODE_CREATE | 2666 ParcelFileDescriptor.MODE_TRUNCATE); 2667 2668 // Initiate the target's backup pass 2669 addBackupTrace("setting timeout"); 2670 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this); 2671 addBackupTrace("calling agent doBackup()"); 2672 agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder); 2673 } catch (Exception e) { 2674 Slog.e(TAG, "Error invoking for backup on " + packageName); 2675 addBackupTrace("exception: " + e); 2676 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, 2677 e.toString()); 2678 agentErrorCleanup(); 2679 return BackupTransport.AGENT_ERROR; 2680 } 2681 2682 // At this point the agent is off and running. The next thing to happen will 2683 // either be a callback from the agent, at which point we'll process its data 2684 // for transport, or a timeout. Either way the next phase will happen in 2685 // response to the TimeoutHandler interface callbacks. 2686 addBackupTrace("invoke success"); 2687 return BackupTransport.TRANSPORT_OK; 2688 } 2689 2690 public void failAgent(IBackupAgent agent, String message) { 2691 try { 2692 agent.fail(message); 2693 } catch (Exception e) { 2694 Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName); 2695 } 2696 } 2697 2698 @Override 2699 public void operationComplete() { 2700 // Okay, the agent successfully reported back to us! 2701 final String pkgName = mCurrentPackage.packageName; 2702 final long filepos = mBackupDataName.length(); 2703 FileDescriptor fd = mBackupData.getFileDescriptor(); 2704 try { 2705 // If it's a 3rd party app, see whether they wrote any protected keys 2706 // and complain mightily if they are attempting shenanigans. 2707 if (mCurrentPackage.applicationInfo != null && 2708 (mCurrentPackage.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) { 2709 ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName, 2710 ParcelFileDescriptor.MODE_READ_ONLY); 2711 BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor()); 2712 try { 2713 while (in.readNextHeader()) { 2714 final String key = in.getKey(); 2715 if (key != null && key.charAt(0) >= 0xff00) { 2716 // Not okay: crash them and bail. 2717 failAgent(mAgentBinder, "Illegal backup key: " + key); 2718 addBackupTrace("illegal key " + key + " from " + pkgName); 2719 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName, 2720 "bad key"); 2721 mBackupHandler.removeMessages(MSG_TIMEOUT); 2722 agentErrorCleanup(); 2723 // agentErrorCleanup() implicitly executes next state properly 2724 return; 2725 } 2726 in.skipEntityData(); 2727 } 2728 } finally { 2729 if (readFd != null) { 2730 readFd.close(); 2731 } 2732 } 2733 } 2734 2735 // Piggyback the widget state payload, if any 2736 BackupDataOutput out = new BackupDataOutput(fd); 2737 byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, 2738 UserHandle.USER_OWNER); 2739 if (widgetState != null) { 2740 out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length); 2741 out.writeEntityData(widgetState, widgetState.length); 2742 } else { 2743 // No widget state for this app, but push a 'delete' operation for it 2744 // in case they're trying to play games with the payload. 2745 out.writeEntityHeader(KEY_WIDGET_STATE, -1); 2746 } 2747 } catch (IOException e) { 2748 // Hard disk error; recovery/failure policy TBD. For now roll back, 2749 // but we may want to consider this a transport-level failure (i.e. 2750 // we're in such a bad state that we can't contemplate doing backup 2751 // operations any more during this pass). 2752 Slog.w(TAG, "Unable to save widget state for " + pkgName); 2753 try { 2754 Os.ftruncate(fd, filepos); 2755 } catch (ErrnoException ee) { 2756 Slog.w(TAG, "Unable to roll back!"); 2757 } 2758 } 2759 2760 // Spin the data off to the transport and proceed with the next stage. 2761 if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for " 2762 + pkgName); 2763 mBackupHandler.removeMessages(MSG_TIMEOUT); 2764 clearAgentState(); 2765 addBackupTrace("operation complete"); 2766 2767 ParcelFileDescriptor backupData = null; 2768 mStatus = BackupTransport.TRANSPORT_OK; 2769 try { 2770 int size = (int) mBackupDataName.length(); 2771 if (size > 0) { 2772 if (mStatus == BackupTransport.TRANSPORT_OK) { 2773 backupData = ParcelFileDescriptor.open(mBackupDataName, 2774 ParcelFileDescriptor.MODE_READ_ONLY); 2775 addBackupTrace("sending data to transport"); 2776 mStatus = mTransport.performBackup(mCurrentPackage, backupData); 2777 } 2778 2779 // TODO - We call finishBackup() for each application backed up, because 2780 // we need to know now whether it succeeded or failed. Instead, we should 2781 // hold off on finishBackup() until the end, which implies holding off on 2782 // renaming *all* the output state files (see below) until that happens. 2783 2784 addBackupTrace("data delivered: " + mStatus); 2785 if (mStatus == BackupTransport.TRANSPORT_OK) { 2786 addBackupTrace("finishing op on transport"); 2787 mStatus = mTransport.finishBackup(); 2788 addBackupTrace("finished: " + mStatus); 2789 } 2790 } else { 2791 if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport"); 2792 addBackupTrace("no data to send"); 2793 } 2794 2795 // After successful transport, delete the now-stale data 2796 // and juggle the files so that next time we supply the agent 2797 // with the new state file it just created. 2798 if (mStatus == BackupTransport.TRANSPORT_OK) { 2799 mBackupDataName.delete(); 2800 mNewStateName.renameTo(mSavedStateName); 2801 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size); 2802 logBackupComplete(pkgName); 2803 } else { 2804 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); 2805 } 2806 } catch (Exception e) { 2807 Slog.e(TAG, "Transport error backing up " + pkgName, e); 2808 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); 2809 mStatus = BackupTransport.TRANSPORT_ERROR; 2810 } finally { 2811 try { if (backupData != null) backupData.close(); } catch (IOException e) {} 2812 } 2813 2814 // If we encountered an error here it's a transport-level failure. That 2815 // means we need to halt everything and reschedule everything for next time. 2816 final BackupState nextState; 2817 if (mStatus != BackupTransport.TRANSPORT_OK) { 2818 revertAndEndBackup(); 2819 nextState = BackupState.FINAL; 2820 } else { 2821 // Success! Proceed with the next app if any, otherwise we're done. 2822 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE; 2823 } 2824 2825 executeNextState(nextState); 2826 } 2827 2828 @Override 2829 public void handleTimeout() { 2830 // Whoops, the current agent timed out running doBackup(). Tidy up and restage 2831 // it for the next time we run a backup pass. 2832 // !!! TODO: keep track of failure counts per agent, and blacklist those which 2833 // fail repeatedly (i.e. have proved themselves to be buggy). 2834 Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName); 2835 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName, 2836 "timeout"); 2837 addBackupTrace("timeout of " + mCurrentPackage.packageName); 2838 agentErrorCleanup(); 2839 dataChangedImpl(mCurrentPackage.packageName); 2840 } 2841 2842 void revertAndEndBackup() { 2843 if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything"); 2844 addBackupTrace("transport error; reverting"); 2845 for (BackupRequest request : mOriginalQueue) { 2846 dataChangedImpl(request.packageName); 2847 } 2848 // We also want to reset the backup schedule based on whatever 2849 // the transport suggests by way of retry/backoff time. 2850 restartBackupAlarm(); 2851 } 2852 2853 void agentErrorCleanup() { 2854 mBackupDataName.delete(); 2855 mNewStateName.delete(); 2856 clearAgentState(); 2857 2858 executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE); 2859 } 2860 2861 // Cleanup common to both success and failure cases 2862 void clearAgentState() { 2863 try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {} 2864 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 2865 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 2866 synchronized (mCurrentOpLock) { 2867 // Current-operation callback handling requires the validity of these various 2868 // bits of internal state as an invariant of the operation still being live. 2869 // This means we make sure to clear all of the state in unison inside the lock. 2870 mCurrentOperations.clear(); 2871 mSavedState = mBackupData = mNewState = null; 2872 } 2873 2874 // If this was a pseudopackage there's no associated Activity Manager state 2875 if (mCurrentPackage.applicationInfo != null) { 2876 addBackupTrace("unbinding " + mCurrentPackage.packageName); 2877 try { // unbind even on timeout, just in case 2878 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 2879 } catch (RemoteException e) { /* can't happen; activity manager is local */ } 2880 } 2881 } 2882 2883 void restartBackupAlarm() { 2884 addBackupTrace("setting backup trigger"); 2885 synchronized (mQueueLock) { 2886 try { 2887 startBackupAlarmsLocked(mTransport.requestBackupTime()); 2888 } catch (RemoteException e) { /* cannot happen */ } 2889 } 2890 } 2891 2892 void executeNextState(BackupState nextState) { 2893 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 2894 + this + " nextState=" + nextState); 2895 addBackupTrace("executeNextState => " + nextState); 2896 mCurrentState = nextState; 2897 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 2898 mBackupHandler.sendMessage(msg); 2899 } 2900 } 2901 2902 2903 // ----- Full backup/restore to a file/socket ----- 2904 2905 class FullBackupObbConnection implements ServiceConnection { 2906 volatile IObbBackupService mService; 2907 2908 FullBackupObbConnection() { 2909 mService = null; 2910 } 2911 2912 public void establish() { 2913 if (DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this); 2914 Intent obbIntent = new Intent().setComponent(new ComponentName( 2915 "com.android.sharedstoragebackup", 2916 "com.android.sharedstoragebackup.ObbBackupService")); 2917 BackupManagerService.this.mContext.bindService( 2918 obbIntent, this, Context.BIND_AUTO_CREATE); 2919 } 2920 2921 public void tearDown() { 2922 BackupManagerService.this.mContext.unbindService(this); 2923 } 2924 2925 public boolean backupObbs(PackageInfo pkg, OutputStream out) { 2926 boolean success = false; 2927 waitForConnection(); 2928 2929 ParcelFileDescriptor[] pipes = null; 2930 try { 2931 pipes = ParcelFileDescriptor.createPipe(); 2932 int token = generateToken(); 2933 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 2934 mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder); 2935 routeSocketDataToOutput(pipes[0], out); 2936 success = waitUntilOperationComplete(token); 2937 } catch (Exception e) { 2938 Slog.w(TAG, "Unable to back up OBBs for " + pkg, e); 2939 } finally { 2940 try { 2941 out.flush(); 2942 if (pipes != null) { 2943 if (pipes[0] != null) pipes[0].close(); 2944 if (pipes[1] != null) pipes[1].close(); 2945 } 2946 } catch (IOException e) { 2947 Slog.w(TAG, "I/O error closing down OBB backup", e); 2948 } 2949 } 2950 return success; 2951 } 2952 2953 public void restoreObbFile(String pkgName, ParcelFileDescriptor data, 2954 long fileSize, int type, String path, long mode, long mtime, 2955 int token, IBackupManager callbackBinder) { 2956 waitForConnection(); 2957 2958 try { 2959 mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime, 2960 token, callbackBinder); 2961 } catch (Exception e) { 2962 Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e); 2963 } 2964 } 2965 2966 private void waitForConnection() { 2967 synchronized (this) { 2968 while (mService == null) { 2969 if (DEBUG) Slog.i(TAG, "...waiting for OBB service binding..."); 2970 try { 2971 this.wait(); 2972 } catch (InterruptedException e) { /* never interrupted */ } 2973 } 2974 if (DEBUG) Slog.i(TAG, "Connected to OBB service; continuing"); 2975 } 2976 } 2977 2978 @Override 2979 public void onServiceConnected(ComponentName name, IBinder service) { 2980 synchronized (this) { 2981 mService = IObbBackupService.Stub.asInterface(service); 2982 if (DEBUG) Slog.i(TAG, "OBB service connection " + mService 2983 + " connected on " + this); 2984 this.notifyAll(); 2985 } 2986 } 2987 2988 @Override 2989 public void onServiceDisconnected(ComponentName name) { 2990 synchronized (this) { 2991 mService = null; 2992 if (DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this); 2993 this.notifyAll(); 2994 } 2995 } 2996 2997 } 2998 2999 private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out) 3000 throws IOException { 3001 FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor()); 3002 DataInputStream in = new DataInputStream(raw); 3003 3004 byte[] buffer = new byte[32 * 1024]; 3005 int chunkTotal; 3006 while ((chunkTotal = in.readInt()) > 0) { 3007 while (chunkTotal > 0) { 3008 int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal; 3009 int nRead = in.read(buffer, 0, toRead); 3010 out.write(buffer, 0, nRead); 3011 chunkTotal -= nRead; 3012 } 3013 } 3014 } 3015 3016 // Core logic for performing one package's full backup, gathering the tarball from the 3017 // application and emitting it to the designated OutputStream. 3018 class FullBackupEngine { 3019 OutputStream mOutput; 3020 IFullBackupRestoreObserver mObserver; 3021 File mFilesDir; 3022 File mManifestFile; 3023 File mMetadataFile; 3024 boolean mIncludeApks; 3025 3026 class FullBackupRunner implements Runnable { 3027 PackageInfo mPackage; 3028 byte[] mWidgetData; 3029 IBackupAgent mAgent; 3030 ParcelFileDescriptor mPipe; 3031 int mToken; 3032 boolean mSendApk; 3033 boolean mWriteManifest; 3034 3035 FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe, 3036 int token, boolean sendApk, boolean writeManifest, byte[] widgetData) 3037 throws IOException { 3038 mPackage = pack; 3039 mWidgetData = widgetData; 3040 mAgent = agent; 3041 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor()); 3042 mToken = token; 3043 mSendApk = sendApk; 3044 mWriteManifest = writeManifest; 3045 } 3046 3047 @Override 3048 public void run() { 3049 try { 3050 BackupDataOutput output = new BackupDataOutput( 3051 mPipe.getFileDescriptor()); 3052 3053 if (mWriteManifest) { 3054 final boolean writeWidgetData = mWidgetData != null; 3055 if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName); 3056 writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData); 3057 FullBackup.backupToTar(mPackage.packageName, null, null, 3058 mFilesDir.getAbsolutePath(), 3059 mManifestFile.getAbsolutePath(), 3060 output); 3061 mManifestFile.delete(); 3062 3063 // We only need to write a metadata file if we have widget data to stash 3064 if (writeWidgetData) { 3065 writeMetadata(mPackage, mMetadataFile, mWidgetData); 3066 FullBackup.backupToTar(mPackage.packageName, null, null, 3067 mFilesDir.getAbsolutePath(), 3068 mMetadataFile.getAbsolutePath(), 3069 output); 3070 mMetadataFile.delete(); 3071 } 3072 } 3073 3074 if (mSendApk) { 3075 writeApkToBackup(mPackage, output); 3076 } 3077 3078 if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName); 3079 prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null); 3080 mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder); 3081 } catch (IOException e) { 3082 Slog.e(TAG, "Error running full backup for " + mPackage.packageName); 3083 } catch (RemoteException e) { 3084 Slog.e(TAG, "Remote agent vanished during full backup of " 3085 + mPackage.packageName); 3086 } finally { 3087 try { 3088 mPipe.close(); 3089 } catch (IOException e) {} 3090 } 3091 } 3092 } 3093 3094 FullBackupEngine(OutputStream output, String packageName, boolean alsoApks) { 3095 mOutput = output; 3096 mIncludeApks = alsoApks; 3097 mFilesDir = new File("/data/system"); 3098 mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME); 3099 mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME); 3100 } 3101 3102 3103 public int backupOnePackage(PackageInfo pkg) throws RemoteException { 3104 int result = BackupTransport.TRANSPORT_OK; 3105 Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName); 3106 3107 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo, 3108 IApplicationThread.BACKUP_MODE_FULL); 3109 if (agent != null) { 3110 ParcelFileDescriptor[] pipes = null; 3111 try { 3112 pipes = ParcelFileDescriptor.createPipe(); 3113 3114 ApplicationInfo app = pkg.applicationInfo; 3115 final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE); 3116 final boolean sendApk = mIncludeApks 3117 && !isSharedStorage 3118 && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0) 3119 && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 || 3120 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); 3121 3122 byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName, 3123 UserHandle.USER_OWNER); 3124 3125 final int token = generateToken(); 3126 FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1], 3127 token, sendApk, !isSharedStorage, widgetBlob); 3128 pipes[1].close(); // the runner has dup'd it 3129 pipes[1] = null; 3130 Thread t = new Thread(runner, "app-data-runner"); 3131 t.start(); 3132 3133 // Now pull data from the app and stuff it into the output 3134 try { 3135 routeSocketDataToOutput(pipes[0], mOutput); 3136 } catch (IOException e) { 3137 Slog.i(TAG, "Caught exception reading from agent", e); 3138 result = BackupTransport.AGENT_ERROR; 3139 } 3140 3141 if (!waitUntilOperationComplete(token)) { 3142 Slog.e(TAG, "Full backup failed on package " + pkg.packageName); 3143 result = BackupTransport.AGENT_ERROR; 3144 } else { 3145 if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName); 3146 } 3147 3148 } catch (IOException e) { 3149 Slog.e(TAG, "Error backing up " + pkg.packageName, e); 3150 result = BackupTransport.AGENT_ERROR; 3151 } finally { 3152 try { 3153 // flush after every package 3154 mOutput.flush(); 3155 if (pipes != null) { 3156 if (pipes[0] != null) pipes[0].close(); 3157 if (pipes[1] != null) pipes[1].close(); 3158 } 3159 } catch (IOException e) { 3160 Slog.w(TAG, "Error bringing down backup stack"); 3161 result = BackupTransport.TRANSPORT_ERROR; 3162 } 3163 } 3164 } else { 3165 Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName); 3166 result = BackupTransport.AGENT_ERROR; 3167 } 3168 tearDown(pkg); 3169 return result; 3170 } 3171 3172 private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) { 3173 // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here 3174 // TODO: handle backing up split APKs 3175 final String appSourceDir = pkg.applicationInfo.getBaseCodePath(); 3176 final String apkDir = new File(appSourceDir).getParent(); 3177 FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null, 3178 apkDir, appSourceDir, output); 3179 3180 // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM 3181 // doesn't have access to external storage. 3182 3183 // Save associated .obb content if it exists and we did save the apk 3184 // check for .obb and save those too 3185 final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_OWNER); 3186 final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0]; 3187 if (obbDir != null) { 3188 if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); 3189 File[] obbFiles = obbDir.listFiles(); 3190 if (obbFiles != null) { 3191 final String obbDirName = obbDir.getAbsolutePath(); 3192 for (File obb : obbFiles) { 3193 FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null, 3194 obbDirName, obb.getAbsolutePath(), output); 3195 } 3196 } 3197 } 3198 } 3199 3200 private void writeAppManifest(PackageInfo pkg, File manifestFile, 3201 boolean withApk, boolean withWidgets) throws IOException { 3202 // Manifest format. All data are strings ending in LF: 3203 // BACKUP_MANIFEST_VERSION, currently 1 3204 // 3205 // Version 1: 3206 // package name 3207 // package's versionCode 3208 // platform versionCode 3209 // getInstallerPackageName() for this package (maybe empty) 3210 // boolean: "1" if archive includes .apk; any other string means not 3211 // number of signatures == N 3212 // N*: signature byte array in ascii format per Signature.toCharsString() 3213 StringBuilder builder = new StringBuilder(4096); 3214 StringBuilderPrinter printer = new StringBuilderPrinter(builder); 3215 3216 printer.println(Integer.toString(BACKUP_MANIFEST_VERSION)); 3217 printer.println(pkg.packageName); 3218 printer.println(Integer.toString(pkg.versionCode)); 3219 printer.println(Integer.toString(Build.VERSION.SDK_INT)); 3220 3221 String installerName = mPackageManager.getInstallerPackageName(pkg.packageName); 3222 printer.println((installerName != null) ? installerName : ""); 3223 3224 printer.println(withApk ? "1" : "0"); 3225 if (pkg.signatures == null) { 3226 printer.println("0"); 3227 } else { 3228 printer.println(Integer.toString(pkg.signatures.length)); 3229 for (Signature sig : pkg.signatures) { 3230 printer.println(sig.toCharsString()); 3231 } 3232 } 3233 3234 FileOutputStream outstream = new FileOutputStream(manifestFile); 3235 outstream.write(builder.toString().getBytes()); 3236 outstream.close(); 3237 3238 // We want the manifest block in the archive stream to be idempotent: 3239 // each time we generate a backup stream for the app, we want the manifest 3240 // block to be identical. The underlying tar mechanism sees it as a file, 3241 // though, and will propagate its mtime, causing the tar header to vary. 3242 // Avoid this problem by pinning the mtime to zero. 3243 manifestFile.setLastModified(0); 3244 } 3245 3246 // Widget metadata format. All header entries are strings ending in LF: 3247 // 3248 // Version 1 header: 3249 // BACKUP_METADATA_VERSION, currently "1" 3250 // package name 3251 // 3252 // File data (all integers are binary in network byte order) 3253 // *N: 4 : integer token identifying which metadata blob 3254 // 4 : integer size of this blob = N 3255 // N : raw bytes of this metadata blob 3256 // 3257 // Currently understood blobs (always in network byte order): 3258 // 3259 // widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN) 3260 // 3261 // Unrecognized blobs are *ignored*, not errors. 3262 private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData) 3263 throws IOException { 3264 StringBuilder b = new StringBuilder(512); 3265 StringBuilderPrinter printer = new StringBuilderPrinter(b); 3266 printer.println(Integer.toString(BACKUP_METADATA_VERSION)); 3267 printer.println(pkg.packageName); 3268 3269 FileOutputStream fout = new FileOutputStream(destination); 3270 BufferedOutputStream bout = new BufferedOutputStream(fout); 3271 DataOutputStream out = new DataOutputStream(bout); 3272 bout.write(b.toString().getBytes()); // bypassing DataOutputStream 3273 3274 if (widgetData != null && widgetData.length > 0) { 3275 out.writeInt(BACKUP_WIDGET_METADATA_TOKEN); 3276 out.writeInt(widgetData.length); 3277 out.write(widgetData); 3278 } 3279 bout.flush(); 3280 out.close(); 3281 3282 // As with the manifest file, guarantee idempotence of the archive metadata 3283 // for the widget block by using a fixed mtime on the transient file. 3284 destination.setLastModified(0); 3285 } 3286 3287 private void tearDown(PackageInfo pkg) { 3288 if (pkg != null) { 3289 final ApplicationInfo app = pkg.applicationInfo; 3290 if (app != null) { 3291 try { 3292 // unbind and tidy up even on timeout or failure, just in case 3293 mActivityManager.unbindBackupAgent(app); 3294 3295 // The agent was running with a stub Application object, so shut it down. 3296 if (app.uid != Process.SYSTEM_UID 3297 && app.uid != Process.PHONE_UID) { 3298 if (MORE_DEBUG) Slog.d(TAG, "Backup complete, killing host process"); 3299 mActivityManager.killApplicationProcess(app.processName, app.uid); 3300 } else { 3301 if (MORE_DEBUG) Slog.d(TAG, "Not killing after backup: " + app.processName); 3302 } 3303 } catch (RemoteException e) { 3304 Slog.d(TAG, "Lost app trying to shut down"); 3305 } 3306 } 3307 } 3308 } 3309 } 3310 3311 // Generic driver skeleton for full backup operations 3312 abstract class FullBackupTask implements Runnable { 3313 IFullBackupRestoreObserver mObserver; 3314 3315 FullBackupTask(IFullBackupRestoreObserver observer) { 3316 mObserver = observer; 3317 } 3318 3319 // wrappers for observer use 3320 final void sendStartBackup() { 3321 if (mObserver != null) { 3322 try { 3323 mObserver.onStartBackup(); 3324 } catch (RemoteException e) { 3325 Slog.w(TAG, "full backup observer went away: startBackup"); 3326 mObserver = null; 3327 } 3328 } 3329 } 3330 3331 final void sendOnBackupPackage(String name) { 3332 if (mObserver != null) { 3333 try { 3334 // TODO: use a more user-friendly name string 3335 mObserver.onBackupPackage(name); 3336 } catch (RemoteException e) { 3337 Slog.w(TAG, "full backup observer went away: backupPackage"); 3338 mObserver = null; 3339 } 3340 } 3341 } 3342 3343 final void sendEndBackup() { 3344 if (mObserver != null) { 3345 try { 3346 mObserver.onEndBackup(); 3347 } catch (RemoteException e) { 3348 Slog.w(TAG, "full backup observer went away: endBackup"); 3349 mObserver = null; 3350 } 3351 } 3352 } 3353 } 3354 3355 boolean deviceIsEncrypted() { 3356 try { 3357 return mMountService.getEncryptionState() 3358 != IMountService.ENCRYPTION_STATE_NONE 3359 && mMountService.getPasswordType() 3360 != StorageManager.CRYPT_TYPE_DEFAULT; 3361 } catch (Exception e) { 3362 // If we can't talk to the mount service we have a serious problem; fail 3363 // "secure" i.e. assuming that the device is encrypted. 3364 Slog.e(TAG, "Unable to communicate with mount service: " + e.getMessage()); 3365 return true; 3366 } 3367 } 3368 3369 // Full backup task variant used for adb backup 3370 class PerformAdbBackupTask extends FullBackupTask { 3371 FullBackupEngine mBackupEngine; 3372 final AtomicBoolean mLatch; 3373 3374 ParcelFileDescriptor mOutputFile; 3375 DeflaterOutputStream mDeflater; 3376 boolean mIncludeApks; 3377 boolean mIncludeObbs; 3378 boolean mIncludeShared; 3379 boolean mDoWidgets; 3380 boolean mAllApps; 3381 boolean mIncludeSystem; 3382 boolean mCompress; 3383 ArrayList<String> mPackages; 3384 String mCurrentPassword; 3385 String mEncryptPassword; 3386 3387 PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, 3388 boolean includeApks, boolean includeObbs, boolean includeShared, 3389 boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps, 3390 boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch) { 3391 super(observer); 3392 mLatch = latch; 3393 3394 mOutputFile = fd; 3395 mIncludeApks = includeApks; 3396 mIncludeObbs = includeObbs; 3397 mIncludeShared = includeShared; 3398 mDoWidgets = doWidgets; 3399 mAllApps = doAllApps; 3400 mIncludeSystem = doSystem; 3401 mPackages = (packages == null) 3402 ? new ArrayList<String>() 3403 : new ArrayList<String>(Arrays.asList(packages)); 3404 mCurrentPassword = curPassword; 3405 // when backing up, if there is a current backup password, we require that 3406 // the user use a nonempty encryption password as well. if one is supplied 3407 // in the UI we use that, but if the UI was left empty we fall back to the 3408 // current backup password (which was supplied by the user as well). 3409 if (encryptPassword == null || "".equals(encryptPassword)) { 3410 mEncryptPassword = curPassword; 3411 } else { 3412 mEncryptPassword = encryptPassword; 3413 } 3414 mCompress = doCompress; 3415 } 3416 3417 void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) { 3418 for (String pkgName : pkgNames) { 3419 if (!set.containsKey(pkgName)) { 3420 try { 3421 PackageInfo info = mPackageManager.getPackageInfo(pkgName, 3422 PackageManager.GET_SIGNATURES); 3423 set.put(pkgName, info); 3424 } catch (NameNotFoundException e) { 3425 Slog.w(TAG, "Unknown package " + pkgName + ", skipping"); 3426 } 3427 } 3428 } 3429 } 3430 3431 private OutputStream emitAesBackupHeader(StringBuilder headerbuf, 3432 OutputStream ofstream) throws Exception { 3433 // User key will be used to encrypt the master key. 3434 byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE); 3435 SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt, 3436 PBKDF2_HASH_ROUNDS); 3437 3438 // the master key is random for each backup 3439 byte[] masterPw = new byte[256 / 8]; 3440 mRng.nextBytes(masterPw); 3441 byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE); 3442 3443 // primary encryption of the datastream with the random key 3444 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 3445 SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES"); 3446 c.init(Cipher.ENCRYPT_MODE, masterKeySpec); 3447 OutputStream finalOutput = new CipherOutputStream(ofstream, c); 3448 3449 // line 4: name of encryption algorithm 3450 headerbuf.append(ENCRYPTION_ALGORITHM_NAME); 3451 headerbuf.append('\n'); 3452 // line 5: user password salt [hex] 3453 headerbuf.append(byteArrayToHex(newUserSalt)); 3454 headerbuf.append('\n'); 3455 // line 6: master key checksum salt [hex] 3456 headerbuf.append(byteArrayToHex(checksumSalt)); 3457 headerbuf.append('\n'); 3458 // line 7: number of PBKDF2 rounds used [decimal] 3459 headerbuf.append(PBKDF2_HASH_ROUNDS); 3460 headerbuf.append('\n'); 3461 3462 // line 8: IV of the user key [hex] 3463 Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding"); 3464 mkC.init(Cipher.ENCRYPT_MODE, userKey); 3465 3466 byte[] IV = mkC.getIV(); 3467 headerbuf.append(byteArrayToHex(IV)); 3468 headerbuf.append('\n'); 3469 3470 // line 9: master IV + key blob, encrypted by the user key [hex]. Blob format: 3471 // [byte] IV length = Niv 3472 // [array of Niv bytes] IV itself 3473 // [byte] master key length = Nmk 3474 // [array of Nmk bytes] master key itself 3475 // [byte] MK checksum hash length = Nck 3476 // [array of Nck bytes] master key checksum hash 3477 // 3478 // The checksum is the (master key + checksum salt), run through the 3479 // stated number of PBKDF2 rounds 3480 IV = c.getIV(); 3481 byte[] mk = masterKeySpec.getEncoded(); 3482 byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(), 3483 checksumSalt, PBKDF2_HASH_ROUNDS); 3484 3485 ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length 3486 + checksum.length + 3); 3487 DataOutputStream mkOut = new DataOutputStream(blob); 3488 mkOut.writeByte(IV.length); 3489 mkOut.write(IV); 3490 mkOut.writeByte(mk.length); 3491 mkOut.write(mk); 3492 mkOut.writeByte(checksum.length); 3493 mkOut.write(checksum); 3494 mkOut.flush(); 3495 byte[] encryptedMk = mkC.doFinal(blob.toByteArray()); 3496 headerbuf.append(byteArrayToHex(encryptedMk)); 3497 headerbuf.append('\n'); 3498 3499 return finalOutput; 3500 } 3501 3502 private void finalizeBackup(OutputStream out) { 3503 try { 3504 // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes. 3505 byte[] eof = new byte[512 * 2]; // newly allocated == zero filled 3506 out.write(eof); 3507 } catch (IOException e) { 3508 Slog.w(TAG, "Error attempting to finalize backup stream"); 3509 } 3510 } 3511 3512 @Override 3513 public void run() { 3514 Slog.i(TAG, "--- Performing full-dataset adb backup ---"); 3515 3516 TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>(); 3517 FullBackupObbConnection obbConnection = new FullBackupObbConnection(); 3518 obbConnection.establish(); // we'll want this later 3519 3520 sendStartBackup(); 3521 3522 // doAllApps supersedes the package set if any 3523 if (mAllApps) { 3524 List<PackageInfo> allPackages = mPackageManager.getInstalledPackages( 3525 PackageManager.GET_SIGNATURES); 3526 for (int i = 0; i < allPackages.size(); i++) { 3527 PackageInfo pkg = allPackages.get(i); 3528 // Exclude system apps if we've been asked to do so 3529 if (mIncludeSystem == true 3530 || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) { 3531 packagesToBackup.put(pkg.packageName, pkg); 3532 } 3533 } 3534 } 3535 3536 // If we're doing widget state as well, ensure that we have all the involved 3537 // host & provider packages in the set 3538 if (mDoWidgets) { 3539 List<String> pkgs = 3540 AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_OWNER); 3541 if (pkgs != null) { 3542 if (MORE_DEBUG) { 3543 Slog.i(TAG, "Adding widget participants to backup set:"); 3544 StringBuilder sb = new StringBuilder(128); 3545 sb.append(" "); 3546 for (String s : pkgs) { 3547 sb.append(' '); 3548 sb.append(s); 3549 } 3550 Slog.i(TAG, sb.toString()); 3551 } 3552 addPackagesToSet(packagesToBackup, pkgs); 3553 } 3554 } 3555 3556 // Now process the command line argument packages, if any. Note that explicitly- 3557 // named system-partition packages will be included even if includeSystem was 3558 // set to false. 3559 if (mPackages != null) { 3560 addPackagesToSet(packagesToBackup, mPackages); 3561 } 3562 3563 // Now we cull any inapplicable / inappropriate packages from the set. This 3564 // includes the special shared-storage agent package; we handle that one 3565 // explicitly at the end of the backup pass. 3566 Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator(); 3567 while (iter.hasNext()) { 3568 PackageInfo pkg = iter.next().getValue(); 3569 if (!appIsEligibleForBackup(pkg.applicationInfo)) { 3570 iter.remove(); 3571 } 3572 } 3573 3574 // flatten the set of packages now so we can explicitly control the ordering 3575 ArrayList<PackageInfo> backupQueue = 3576 new ArrayList<PackageInfo>(packagesToBackup.values()); 3577 FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor()); 3578 OutputStream out = null; 3579 3580 PackageInfo pkg = null; 3581 try { 3582 boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0); 3583 3584 // Only allow encrypted backups of encrypted devices 3585 if (deviceIsEncrypted() && !encrypting) { 3586 Slog.e(TAG, "Unencrypted backup of encrypted device; aborting"); 3587 return; 3588 } 3589 3590 OutputStream finalOutput = ofstream; 3591 3592 // Verify that the given password matches the currently-active 3593 // backup password, if any 3594 if (!backupPasswordMatches(mCurrentPassword)) { 3595 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 3596 return; 3597 } 3598 3599 // Write the global file header. All strings are UTF-8 encoded; lines end 3600 // with a '\n' byte. Actual backup data begins immediately following the 3601 // final '\n'. 3602 // 3603 // line 1: "ANDROID BACKUP" 3604 // line 2: backup file format version, currently "2" 3605 // line 3: compressed? "0" if not compressed, "1" if compressed. 3606 // line 4: name of encryption algorithm [currently only "none" or "AES-256"] 3607 // 3608 // When line 4 is not "none", then additional header data follows: 3609 // 3610 // line 5: user password salt [hex] 3611 // line 6: master key checksum salt [hex] 3612 // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal] 3613 // line 8: IV of the user key [hex] 3614 // line 9: master key blob [hex] 3615 // IV of the master key, master key itself, master key checksum hash 3616 // 3617 // The master key checksum is the master key plus its checksum salt, run through 3618 // 10k rounds of PBKDF2. This is used to verify that the user has supplied the 3619 // correct password for decrypting the archive: the master key decrypted from 3620 // the archive using the user-supplied password is also run through PBKDF2 in 3621 // this way, and if the result does not match the checksum as stored in the 3622 // archive, then we know that the user-supplied password does not match the 3623 // archive's. 3624 StringBuilder headerbuf = new StringBuilder(1024); 3625 3626 headerbuf.append(BACKUP_FILE_HEADER_MAGIC); 3627 headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n 3628 headerbuf.append(mCompress ? "\n1\n" : "\n0\n"); 3629 3630 try { 3631 // Set up the encryption stage if appropriate, and emit the correct header 3632 if (encrypting) { 3633 finalOutput = emitAesBackupHeader(headerbuf, finalOutput); 3634 } else { 3635 headerbuf.append("none\n"); 3636 } 3637 3638 byte[] header = headerbuf.toString().getBytes("UTF-8"); 3639 ofstream.write(header); 3640 3641 // Set up the compression stage feeding into the encryption stage (if any) 3642 if (mCompress) { 3643 Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION); 3644 finalOutput = new DeflaterOutputStream(finalOutput, deflater, true); 3645 } 3646 3647 out = finalOutput; 3648 } catch (Exception e) { 3649 // Should never happen! 3650 Slog.e(TAG, "Unable to emit archive header", e); 3651 return; 3652 } 3653 3654 // Shared storage if requested 3655 if (mIncludeShared) { 3656 try { 3657 pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0); 3658 backupQueue.add(pkg); 3659 } catch (NameNotFoundException e) { 3660 Slog.e(TAG, "Unable to find shared-storage backup handler"); 3661 } 3662 } 3663 3664 // Now actually run the constructed backup sequence 3665 int N = backupQueue.size(); 3666 for (int i = 0; i < N; i++) { 3667 pkg = backupQueue.get(i); 3668 final boolean isSharedStorage = 3669 pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE); 3670 3671 mBackupEngine = new FullBackupEngine(out, pkg.packageName, mIncludeApks); 3672 sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName); 3673 mBackupEngine.backupOnePackage(pkg); 3674 3675 // after the app's agent runs to handle its private filesystem 3676 // contents, back up any OBB content it has on its behalf. 3677 if (mIncludeObbs) { 3678 boolean obbOkay = obbConnection.backupObbs(pkg, out); 3679 if (!obbOkay) { 3680 throw new RuntimeException("Failure writing OBB stack for " + pkg); 3681 } 3682 } 3683 } 3684 3685 // Done! 3686 finalizeBackup(out); 3687 } catch (RemoteException e) { 3688 Slog.e(TAG, "App died during full backup"); 3689 } catch (Exception e) { 3690 Slog.e(TAG, "Internal exception during full backup", e); 3691 } finally { 3692 try { 3693 if (out != null) out.close(); 3694 mOutputFile.close(); 3695 } catch (IOException e) { 3696 /* nothing we can do about this */ 3697 } 3698 synchronized (mCurrentOpLock) { 3699 mCurrentOperations.clear(); 3700 } 3701 synchronized (mLatch) { 3702 mLatch.set(true); 3703 mLatch.notifyAll(); 3704 } 3705 sendEndBackup(); 3706 obbConnection.tearDown(); 3707 if (DEBUG) Slog.d(TAG, "Full backup pass complete."); 3708 mWakelock.release(); 3709 } 3710 } 3711 } 3712 3713 // Full backup task extension used for transport-oriented operation 3714 class PerformFullTransportBackupTask extends FullBackupTask { 3715 static final String TAG = "PFTBT"; 3716 ArrayList<PackageInfo> mPackages; 3717 boolean mUpdateSchedule; 3718 AtomicBoolean mLatch; 3719 AtomicBoolean mKeepRunning; // signal from job scheduler 3720 FullBackupJob mJob; // if a scheduled job needs to be finished afterwards 3721 3722 PerformFullTransportBackupTask(IFullBackupRestoreObserver observer, 3723 String[] whichPackages, boolean updateSchedule, 3724 FullBackupJob runningJob, AtomicBoolean latch) { 3725 super(observer); 3726 mUpdateSchedule = updateSchedule; 3727 mLatch = latch; 3728 mKeepRunning = new AtomicBoolean(true); 3729 mJob = runningJob; 3730 mPackages = new ArrayList<PackageInfo>(whichPackages.length); 3731 3732 for (String pkg : whichPackages) { 3733 try { 3734 PackageInfo info = mPackageManager.getPackageInfo(pkg, 3735 PackageManager.GET_SIGNATURES); 3736 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0 3737 || pkg.equals(SHARED_BACKUP_AGENT_PACKAGE)) { 3738 // Cull any packages that have indicated that backups are not permitted, 3739 // as well as any explicit mention of the 'special' shared-storage agent 3740 // package (we handle that one at the end). 3741 if (MORE_DEBUG) { 3742 Slog.d(TAG, "Ignoring opted-out package " + pkg); 3743 } 3744 continue; 3745 } else if ((info.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 3746 && (info.applicationInfo.backupAgentName == null)) { 3747 // Cull any packages that run as system-domain uids but do not define their 3748 // own backup agents 3749 if (MORE_DEBUG) { 3750 Slog.d(TAG, "Ignoring non-agent system package " + pkg); 3751 } 3752 continue; 3753 } 3754 mPackages.add(info); 3755 } catch (NameNotFoundException e) { 3756 Slog.i(TAG, "Requested package " + pkg + " not found; ignoring"); 3757 } 3758 } 3759 } 3760 3761 public void setRunning(boolean running) { 3762 mKeepRunning.set(running); 3763 } 3764 3765 @Override 3766 public void run() { 3767 // data from the app, passed to us for bridging to the transport 3768 ParcelFileDescriptor[] enginePipes = null; 3769 3770 // Pipe through which we write data to the transport 3771 ParcelFileDescriptor[] transportPipes = null; 3772 3773 PackageInfo currentPackage; 3774 3775 try { 3776 IBackupTransport transport = getTransport(mCurrentTransport); 3777 if (transport == null) { 3778 Slog.w(TAG, "Transport not present; full data backup not performed"); 3779 return; 3780 } 3781 3782 // Set up to send data to the transport 3783 final int N = mPackages.size(); 3784 for (int i = 0; i < N; i++) { 3785 currentPackage = mPackages.get(i); 3786 if (DEBUG) { 3787 Slog.i(TAG, "Initiating full-data transport backup of " 3788 + currentPackage.packageName); 3789 } 3790 EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, 3791 currentPackage.packageName); 3792 3793 transportPipes = ParcelFileDescriptor.createPipe(); 3794 3795 // Tell the transport the data's coming 3796 int result = transport.performFullBackup(currentPackage, 3797 transportPipes[0]); 3798 if (result == BackupTransport.TRANSPORT_OK) { 3799 // The transport has its own copy of the read end of the pipe, 3800 // so close ours now 3801 transportPipes[0].close(); 3802 transportPipes[0] = null; 3803 3804 // Now set up the backup engine / data source end of things 3805 enginePipes = ParcelFileDescriptor.createPipe(); 3806 AtomicBoolean runnerLatch = new AtomicBoolean(false); 3807 SinglePackageBackupRunner backupRunner = 3808 new SinglePackageBackupRunner(enginePipes[1], currentPackage, 3809 runnerLatch); 3810 // The runner dup'd the pipe half, so we close it here 3811 enginePipes[1].close(); 3812 enginePipes[1] = null; 3813 3814 // Spin off the runner to fetch the app's data and pipe it 3815 // into the engine pipes 3816 (new Thread(backupRunner, "package-backup-bridge")).start(); 3817 3818 // Read data off the engine pipe and pass it to the transport 3819 // pipe until we hit EOD on the input stream. 3820 FileInputStream in = new FileInputStream( 3821 enginePipes[0].getFileDescriptor()); 3822 FileOutputStream out = new FileOutputStream( 3823 transportPipes[1].getFileDescriptor()); 3824 byte[] buffer = new byte[8192]; 3825 int nRead = 0; 3826 do { 3827 if (!mKeepRunning.get()) { 3828 if (DEBUG_SCHEDULING) { 3829 Slog.i(TAG, "Full backup task told to stop"); 3830 } 3831 break; 3832 } 3833 nRead = in.read(buffer); 3834 if (nRead > 0) { 3835 out.write(buffer, 0, nRead); 3836 result = transport.sendBackupData(nRead); 3837 } 3838 } while (nRead > 0 && result == BackupTransport.TRANSPORT_OK); 3839 3840 // If we've lost our running criteria, tell the transport to cancel 3841 // and roll back this (partial) backup payload; otherwise tell it 3842 // that we've reached the clean finish state. 3843 if (!mKeepRunning.get()) { 3844 result = BackupTransport.TRANSPORT_ERROR; 3845 transport.cancelFullBackup(); 3846 } else { 3847 // If we were otherwise in a good state, now interpret the final 3848 // result based on what finishBackup() returns. If we're in a 3849 // failure case already, preserve that result and ignore whatever 3850 // finishBackup() reports. 3851 final int finishResult = transport.finishBackup(); 3852 if (result == BackupTransport.TRANSPORT_OK) { 3853 result = finishResult; 3854 } 3855 } 3856 3857 if (MORE_DEBUG) { 3858 Slog.i(TAG, "Done trying to send backup data: result=" + result); 3859 } 3860 3861 if (result != BackupTransport.TRANSPORT_OK) { 3862 Slog.e(TAG, "Error " + result 3863 + " backing up " + currentPackage.packageName); 3864 } 3865 } 3866 3867 // Roll this package to the end of the backup queue if we're 3868 // in a queue-driven mode (regardless of success/failure) 3869 if (mUpdateSchedule) { 3870 enqueueFullBackup(currentPackage.packageName, 3871 System.currentTimeMillis()); 3872 } 3873 3874 if (result == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 3875 if (DEBUG) { 3876 Slog.i(TAG, "Transport rejected backup of " 3877 + currentPackage.packageName 3878 + ", skipping"); 3879 } 3880 EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, 3881 currentPackage.packageName, "transport rejected"); 3882 // do nothing, clean up, and continue looping 3883 } else if (result != BackupTransport.TRANSPORT_OK) { 3884 if (DEBUG) { 3885 Slog.i(TAG, "Transport failed; aborting backup: " + result); 3886 EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE); 3887 return; 3888 } 3889 } else { 3890 // Success! 3891 EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, 3892 currentPackage.packageName); 3893 logBackupComplete(currentPackage.packageName); 3894 } 3895 cleanUpPipes(transportPipes); 3896 cleanUpPipes(enginePipes); 3897 currentPackage = null; 3898 } 3899 3900 if (DEBUG) { 3901 Slog.i(TAG, "Full backup completed."); 3902 } 3903 } catch (Exception e) { 3904 Slog.w(TAG, "Exception trying full transport backup", e); 3905 } finally { 3906 cleanUpPipes(transportPipes); 3907 cleanUpPipes(enginePipes); 3908 3909 if (mJob != null) { 3910 mJob.finishBackupPass(); 3911 } 3912 3913 synchronized (mQueueLock) { 3914 mRunningFullBackupTask = null; 3915 } 3916 3917 synchronized (mLatch) { 3918 mLatch.set(true); 3919 mLatch.notifyAll(); 3920 } 3921 3922 // Now that we're actually done with schedule-driven work, reschedule 3923 // the next pass based on the new queue state. 3924 if (mUpdateSchedule) { 3925 scheduleNextFullBackupJob(); 3926 } 3927 } 3928 } 3929 3930 void cleanUpPipes(ParcelFileDescriptor[] pipes) { 3931 if (pipes != null) { 3932 if (pipes[0] != null) { 3933 ParcelFileDescriptor fd = pipes[0]; 3934 pipes[0] = null; 3935 try { 3936 fd.close(); 3937 } catch (IOException e) { 3938 Slog.w(TAG, "Unable to close pipe!"); 3939 } 3940 } 3941 if (pipes[1] != null) { 3942 ParcelFileDescriptor fd = pipes[1]; 3943 pipes[1] = null; 3944 try { 3945 fd.close(); 3946 } catch (IOException e) { 3947 Slog.w(TAG, "Unable to close pipe!"); 3948 } 3949 } 3950 } 3951 } 3952 3953 // Run the backup and pipe it back to the given socket -- expects to run on 3954 // a standalone thread. The runner owns this half of the pipe, and closes 3955 // it to indicate EOD to the other end. 3956 class SinglePackageBackupRunner implements Runnable { 3957 final ParcelFileDescriptor mOutput; 3958 final PackageInfo mTarget; 3959 final AtomicBoolean mLatch; 3960 3961 SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target, 3962 AtomicBoolean latch) throws IOException { 3963 int oldfd = output.getFd(); 3964 mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor()); 3965 mTarget = target; 3966 mLatch = latch; 3967 } 3968 3969 @Override 3970 public void run() { 3971 try { 3972 FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor()); 3973 FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName, false); 3974 engine.backupOnePackage(mTarget); 3975 } catch (Exception e) { 3976 Slog.e(TAG, "Exception during full package backup of " + mTarget); 3977 } finally { 3978 synchronized (mLatch) { 3979 mLatch.set(true); 3980 mLatch.notifyAll(); 3981 } 3982 try { 3983 mOutput.close(); 3984 } catch (IOException e) { 3985 Slog.w(TAG, "Error closing transport pipe in runner"); 3986 } 3987 } 3988 } 3989 3990 } 3991 } 3992 3993 // ----- Full-data backup scheduling ----- 3994 3995 /** 3996 * Schedule a job to tell us when it's a good time to run a full backup 3997 */ 3998 void scheduleNextFullBackupJob() { 3999 synchronized (mQueueLock) { 4000 if (mFullBackupQueue.size() > 0) { 4001 // schedule the next job at the point in the future when the least-recently 4002 // backed up app comes due for backup again; or immediately if it's already 4003 // due. 4004 long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup; 4005 long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup; 4006 final long latency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL) 4007 ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0; 4008 Runnable r = new Runnable() { 4009 @Override public void run() { 4010 FullBackupJob.schedule(mContext, latency); 4011 } 4012 }; 4013 mBackupHandler.postDelayed(r, 2500); 4014 } else { 4015 if (DEBUG_SCHEDULING) { 4016 Slog.i(TAG, "Full backup queue empty; not scheduling"); 4017 } 4018 } 4019 } 4020 } 4021 4022 /** 4023 * Enqueue full backup for the given app, with a note about when it last ran. 4024 */ 4025 void enqueueFullBackup(String packageName, long lastBackedUp) { 4026 FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp); 4027 synchronized (mQueueLock) { 4028 int N = mFullBackupQueue.size(); 4029 // First, sanity check that we aren't adding a duplicate. Slow but 4030 // straightforward; we'll have at most on the order of a few hundred 4031 // items in this list. 4032 for (int i = N-1; i >= 0; i--) { 4033 final FullBackupEntry e = mFullBackupQueue.get(i); 4034 if (packageName.equals(e.packageName)) { 4035 if (DEBUG) { 4036 Slog.w(TAG, "Removing schedule queue dupe of " + packageName); 4037 } 4038 mFullBackupQueue.remove(i); 4039 } 4040 } 4041 4042 // This is also slow but easy for modest numbers of apps: work backwards 4043 // from the end of the queue until we find an item whose last backup 4044 // time was before this one, then insert this new entry after it. 4045 int which; 4046 for (which = mFullBackupQueue.size() - 1; which >= 0; which--) { 4047 final FullBackupEntry entry = mFullBackupQueue.get(which); 4048 if (entry.lastBackup <= lastBackedUp) { 4049 mFullBackupQueue.add(which + 1, newEntry); 4050 break; 4051 } 4052 } 4053 if (which < 0) { 4054 // this one is earlier than any existing one, so prepend 4055 mFullBackupQueue.add(0, newEntry); 4056 } 4057 } 4058 writeFullBackupScheduleAsync(); 4059 } 4060 4061 /** 4062 * Conditions are right for a full backup operation, so run one. The model we use is 4063 * to perform one app backup per scheduled job execution, and to reschedule the job 4064 * with zero latency as long as conditions remain right and we still have work to do. 4065 * 4066 * @return Whether ongoing work will continue. The return value here will be passed 4067 * along as the return value to the scheduled job's onStartJob() callback. 4068 */ 4069 boolean beginFullBackup(FullBackupJob scheduledJob) { 4070 long now = System.currentTimeMillis(); 4071 FullBackupEntry entry = null; 4072 4073 if (DEBUG_SCHEDULING) { 4074 Slog.i(TAG, "Beginning scheduled full backup operation"); 4075 } 4076 4077 // Great; we're able to run full backup jobs now. See if we have any work to do. 4078 synchronized (mQueueLock) { 4079 if (mRunningFullBackupTask != null) { 4080 Slog.e(TAG, "Backup triggered but one already/still running!"); 4081 return false; 4082 } 4083 4084 if (mFullBackupQueue.size() == 0) { 4085 // no work to do so just bow out 4086 if (DEBUG) { 4087 Slog.i(TAG, "Backup queue empty; doing nothing"); 4088 } 4089 return false; 4090 } 4091 4092 entry = mFullBackupQueue.get(0); 4093 long timeSinceRun = now - entry.lastBackup; 4094 if (timeSinceRun < MIN_FULL_BACKUP_INTERVAL) { 4095 // It's too early to back up the next thing in the queue, so bow out 4096 if (MORE_DEBUG) { 4097 Slog.i(TAG, "Device ready but too early to back up next app"); 4098 } 4099 final long latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun; 4100 mBackupHandler.post(new Runnable() { 4101 @Override public void run() { 4102 FullBackupJob.schedule(mContext, latency); 4103 } 4104 }); 4105 return false; 4106 } 4107 4108 // Okay, the top thing is runnable now. Pop it off and get going. 4109 mFullBackupQueue.remove(0); 4110 AtomicBoolean latch = new AtomicBoolean(false); 4111 String[] pkg = new String[] {entry.packageName}; 4112 mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true, 4113 scheduledJob, latch); 4114 (new Thread(mRunningFullBackupTask)).start(); 4115 } 4116 4117 return true; 4118 } 4119 4120 // The job scheduler says our constraints don't hold any more, 4121 // so tear down any ongoing backup task right away. 4122 void endFullBackup() { 4123 synchronized (mQueueLock) { 4124 if (mRunningFullBackupTask != null) { 4125 if (DEBUG_SCHEDULING) { 4126 Slog.i(TAG, "Telling running backup to stop"); 4127 } 4128 mRunningFullBackupTask.setRunning(false); 4129 } 4130 } 4131 } 4132 4133 // ----- Restore infrastructure ----- 4134 4135 abstract class RestoreEngine { 4136 static final String TAG = "RestoreEngine"; 4137 4138 public static final int SUCCESS = 0; 4139 public static final int TARGET_FAILURE = -2; 4140 public static final int TRANSPORT_FAILURE = -3; 4141 4142 private AtomicBoolean mRunning = new AtomicBoolean(false); 4143 private AtomicInteger mResult = new AtomicInteger(SUCCESS); 4144 4145 public boolean isRunning() { 4146 return mRunning.get(); 4147 } 4148 4149 public void setRunning(boolean stillRunning) { 4150 synchronized (mRunning) { 4151 mRunning.set(stillRunning); 4152 mRunning.notifyAll(); 4153 } 4154 } 4155 4156 public int waitForResult() { 4157 synchronized (mRunning) { 4158 while (isRunning()) { 4159 try { 4160 mRunning.wait(); 4161 } catch (InterruptedException e) {} 4162 } 4163 } 4164 return getResult(); 4165 } 4166 4167 public int getResult() { 4168 return mResult.get(); 4169 } 4170 4171 public void setResult(int result) { 4172 mResult.set(result); 4173 } 4174 4175 // TODO: abstract restore state and APIs 4176 } 4177 4178 // ----- Full restore from a file/socket ----- 4179 4180 // Description of a file in the restore datastream 4181 static class FileMetadata { 4182 String packageName; // name of the owning app 4183 String installerPackageName; // name of the market-type app that installed the owner 4184 int type; // e.g. BackupAgent.TYPE_DIRECTORY 4185 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN 4186 String path; // subpath within the semantic domain 4187 long mode; // e.g. 0666 (actually int) 4188 long mtime; // last mod time, UTC time_t (actually int) 4189 long size; // bytes of content 4190 4191 @Override 4192 public String toString() { 4193 StringBuilder sb = new StringBuilder(128); 4194 sb.append("FileMetadata{"); 4195 sb.append(packageName); sb.append(','); 4196 sb.append(type); sb.append(','); 4197 sb.append(domain); sb.append(':'); sb.append(path); sb.append(','); 4198 sb.append(size); 4199 sb.append('}'); 4200 return sb.toString(); 4201 } 4202 } 4203 4204 enum RestorePolicy { 4205 IGNORE, 4206 ACCEPT, 4207 ACCEPT_IF_APK 4208 } 4209 4210 // Full restore engine, used by both adb restore and transport-based full restore 4211 class FullRestoreEngine extends RestoreEngine { 4212 // Dedicated observer, if any 4213 IFullBackupRestoreObserver mObserver; 4214 4215 // Where we're delivering the file data as we go 4216 IBackupAgent mAgent; 4217 4218 // Are we permitted to only deliver a specific package's metadata? 4219 PackageInfo mOnlyPackage; 4220 4221 boolean mAllowApks; 4222 boolean mAllowObbs; 4223 4224 // Which package are we currently handling data for? 4225 String mAgentPackage; 4226 4227 // Info for working with the target app process 4228 ApplicationInfo mTargetApp; 4229 4230 // Machinery for restoring OBBs 4231 FullBackupObbConnection mObbConnection = null; 4232 4233 // possible handling states for a given package in the restore dataset 4234 final HashMap<String, RestorePolicy> mPackagePolicies 4235 = new HashMap<String, RestorePolicy>(); 4236 4237 // installer package names for each encountered app, derived from the manifests 4238 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 4239 4240 // Signatures for a given package found in its manifest file 4241 final HashMap<String, Signature[]> mManifestSignatures 4242 = new HashMap<String, Signature[]>(); 4243 4244 // Packages we've already wiped data on when restoring their first file 4245 final HashSet<String> mClearedPackages = new HashSet<String>(); 4246 4247 // How much data have we moved? 4248 long mBytes; 4249 4250 // Working buffer 4251 byte[] mBuffer; 4252 4253 // Pipes for moving data 4254 ParcelFileDescriptor[] mPipes = null; 4255 4256 // Widget blob to be restored out-of-band 4257 byte[] mWidgetData = null; 4258 4259 // Runner that can be placed in a separate thread to do in-process 4260 // invocations of the full restore API asynchronously 4261 class RestoreFileRunnable implements Runnable { 4262 IBackupAgent mAgent; 4263 FileMetadata mInfo; 4264 ParcelFileDescriptor mSocket; 4265 int mToken; 4266 4267 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 4268 ParcelFileDescriptor socket, int token) throws IOException { 4269 mAgent = agent; 4270 mInfo = info; 4271 mToken = token; 4272 4273 // This class is used strictly for process-local binder invocations. The 4274 // semantics of ParcelFileDescriptor differ in this case; in particular, we 4275 // do not automatically get a 'dup'ed descriptor that we can can continue 4276 // to use asynchronously from the caller. So, we make sure to dup it ourselves 4277 // before proceeding to do the restore. 4278 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 4279 } 4280 4281 @Override 4282 public void run() { 4283 try { 4284 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 4285 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 4286 mToken, mBackupManagerBinder); 4287 } catch (RemoteException e) { 4288 // never happens; this is used strictly for local binder calls 4289 } 4290 } 4291 } 4292 4293 public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage, 4294 boolean allowApks, boolean allowObbs) { 4295 mObserver = observer; 4296 mOnlyPackage = onlyPackage; 4297 mAllowApks = allowApks; 4298 mAllowObbs = allowObbs; 4299 mBuffer = new byte[32 * 1024]; 4300 mBytes = 0; 4301 } 4302 4303 public boolean restoreOneFile(InputStream instream) { 4304 if (!isRunning()) { 4305 Slog.w(TAG, "Restore engine used after halting"); 4306 return false; 4307 } 4308 4309 FileMetadata info; 4310 try { 4311 if (MORE_DEBUG) { 4312 Slog.v(TAG, "Reading tar header for restoring file"); 4313 } 4314 info = readTarHeaders(instream); 4315 if (info != null) { 4316 if (MORE_DEBUG) { 4317 dumpFileMetadata(info); 4318 } 4319 4320 final String pkg = info.packageName; 4321 if (!pkg.equals(mAgentPackage)) { 4322 // In the single-package case, it's a semantic error to expect 4323 // one app's data but see a different app's on the wire 4324 if (mOnlyPackage != null) { 4325 if (!pkg.equals(mOnlyPackage.packageName)) { 4326 Slog.w(TAG, "Expected data for " + mOnlyPackage 4327 + " but saw " + pkg); 4328 setResult(RestoreEngine.TRANSPORT_FAILURE); 4329 setRunning(false); 4330 return false; 4331 } 4332 } 4333 4334 // okay, change in package; set up our various 4335 // bookkeeping if we haven't seen it yet 4336 if (!mPackagePolicies.containsKey(pkg)) { 4337 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4338 } 4339 4340 // Clean up the previous agent relationship if necessary, 4341 // and let the observer know we're considering a new app. 4342 if (mAgent != null) { 4343 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 4344 // Now we're really done 4345 tearDownPipes(); 4346 tearDownAgent(mTargetApp); 4347 mTargetApp = null; 4348 mAgentPackage = null; 4349 } 4350 } 4351 4352 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 4353 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 4354 mPackageInstallers.put(pkg, info.installerPackageName); 4355 // We've read only the manifest content itself at this point, 4356 // so consume the footer before looping around to the next 4357 // input file 4358 skipTarPadding(info.size, instream); 4359 sendOnRestorePackage(pkg); 4360 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 4361 // Metadata blobs! 4362 readMetadata(info, instream); 4363 skipTarPadding(info.size, instream); 4364 } else { 4365 // Non-manifest, so it's actual file data. Is this a package 4366 // we're ignoring? 4367 boolean okay = true; 4368 RestorePolicy policy = mPackagePolicies.get(pkg); 4369 switch (policy) { 4370 case IGNORE: 4371 okay = false; 4372 break; 4373 4374 case ACCEPT_IF_APK: 4375 // If we're in accept-if-apk state, then the first file we 4376 // see MUST be the apk. 4377 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 4378 if (DEBUG) Slog.d(TAG, "APK file; installing"); 4379 // Try to install the app. 4380 String installerName = mPackageInstallers.get(pkg); 4381 okay = installApk(info, installerName, instream); 4382 // good to go; promote to ACCEPT 4383 mPackagePolicies.put(pkg, (okay) 4384 ? RestorePolicy.ACCEPT 4385 : RestorePolicy.IGNORE); 4386 // At this point we've consumed this file entry 4387 // ourselves, so just strip the tar footer and 4388 // go on to the next file in the input stream 4389 skipTarPadding(info.size, instream); 4390 return true; 4391 } else { 4392 // File data before (or without) the apk. We can't 4393 // handle it coherently in this case so ignore it. 4394 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4395 okay = false; 4396 } 4397 break; 4398 4399 case ACCEPT: 4400 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 4401 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 4402 // we can take the data without the apk, so we 4403 // *want* to do so. skip the apk by declaring this 4404 // one file not-okay without changing the restore 4405 // policy for the package. 4406 okay = false; 4407 } 4408 break; 4409 4410 default: 4411 // Something has gone dreadfully wrong when determining 4412 // the restore policy from the manifest. Ignore the 4413 // rest of this package's data. 4414 Slog.e(TAG, "Invalid policy from manifest"); 4415 okay = false; 4416 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4417 break; 4418 } 4419 4420 // Is it a *file* we need to drop? 4421 if (!isRestorableFile(info)) { 4422 okay = false; 4423 } 4424 4425 // If the policy is satisfied, go ahead and set up to pipe the 4426 // data to the agent. 4427 if (DEBUG && okay && mAgent != null) { 4428 Slog.i(TAG, "Reusing existing agent instance"); 4429 } 4430 if (okay && mAgent == null) { 4431 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 4432 4433 try { 4434 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 4435 4436 // If we haven't sent any data to this app yet, we probably 4437 // need to clear it first. Check that. 4438 if (!mClearedPackages.contains(pkg)) { 4439 // apps with their own backup agents are 4440 // responsible for coherently managing a full 4441 // restore. 4442 if (mTargetApp.backupAgentName == null) { 4443 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 4444 clearApplicationDataSynchronous(pkg); 4445 } else { 4446 if (DEBUG) Slog.d(TAG, "backup agent (" 4447 + mTargetApp.backupAgentName + ") => no clear"); 4448 } 4449 mClearedPackages.add(pkg); 4450 } else { 4451 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 4452 } 4453 4454 // All set; now set up the IPC and launch the agent 4455 setUpPipes(); 4456 mAgent = bindToAgentSynchronous(mTargetApp, 4457 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 4458 mAgentPackage = pkg; 4459 } catch (IOException e) { 4460 // fall through to error handling 4461 } catch (NameNotFoundException e) { 4462 // fall through to error handling 4463 } 4464 4465 if (mAgent == null) { 4466 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 4467 okay = false; 4468 tearDownPipes(); 4469 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4470 } 4471 } 4472 4473 // Sanity check: make sure we never give data to the wrong app. This 4474 // should never happen but a little paranoia here won't go amiss. 4475 if (okay && !pkg.equals(mAgentPackage)) { 4476 Slog.e(TAG, "Restoring data for " + pkg 4477 + " but agent is for " + mAgentPackage); 4478 okay = false; 4479 } 4480 4481 // At this point we have an agent ready to handle the full 4482 // restore data as well as a pipe for sending data to 4483 // that agent. Tell the agent to start reading from the 4484 // pipe. 4485 if (okay) { 4486 boolean agentSuccess = true; 4487 long toCopy = info.size; 4488 final int token = generateToken(); 4489 try { 4490 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 4491 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 4492 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 4493 + " : " + info.path); 4494 mObbConnection.restoreObbFile(pkg, mPipes[0], 4495 info.size, info.type, info.path, info.mode, 4496 info.mtime, token, mBackupManagerBinder); 4497 } else { 4498 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 4499 + info.path); 4500 // fire up the app's agent listening on the socket. If 4501 // the agent is running in the system process we can't 4502 // just invoke it asynchronously, so we provide a thread 4503 // for it here. 4504 if (mTargetApp.processName.equals("system")) { 4505 Slog.d(TAG, "system process agent - spinning a thread"); 4506 RestoreFileRunnable runner = new RestoreFileRunnable( 4507 mAgent, info, mPipes[0], token); 4508 new Thread(runner, "restore-sys-runner").start(); 4509 } else { 4510 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 4511 info.domain, info.path, info.mode, info.mtime, 4512 token, mBackupManagerBinder); 4513 } 4514 } 4515 } catch (IOException e) { 4516 // couldn't dup the socket for a process-local restore 4517 Slog.d(TAG, "Couldn't establish restore"); 4518 agentSuccess = false; 4519 okay = false; 4520 } catch (RemoteException e) { 4521 // whoops, remote entity went away. We'll eat the content 4522 // ourselves, then, and not copy it over. 4523 Slog.e(TAG, "Agent crashed during full restore"); 4524 agentSuccess = false; 4525 okay = false; 4526 } 4527 4528 // Copy over the data if the agent is still good 4529 if (okay) { 4530 if (MORE_DEBUG) { 4531 Slog.v(TAG, " copying to restore agent: " 4532 + toCopy + " bytes"); 4533 } 4534 boolean pipeOkay = true; 4535 FileOutputStream pipe = new FileOutputStream( 4536 mPipes[1].getFileDescriptor()); 4537 while (toCopy > 0) { 4538 int toRead = (toCopy > mBuffer.length) 4539 ? mBuffer.length : (int)toCopy; 4540 int nRead = instream.read(mBuffer, 0, toRead); 4541 if (nRead >= 0) mBytes += nRead; 4542 if (nRead <= 0) break; 4543 toCopy -= nRead; 4544 4545 // send it to the output pipe as long as things 4546 // are still good 4547 if (pipeOkay) { 4548 try { 4549 pipe.write(mBuffer, 0, nRead); 4550 } catch (IOException e) { 4551 Slog.e(TAG, "Failed to write to restore pipe", e); 4552 pipeOkay = false; 4553 } 4554 } 4555 } 4556 4557 // done sending that file! Now we just need to consume 4558 // the delta from info.size to the end of block. 4559 skipTarPadding(info.size, instream); 4560 4561 // and now that we've sent it all, wait for the remote 4562 // side to acknowledge receipt 4563 agentSuccess = waitUntilOperationComplete(token); 4564 } 4565 4566 // okay, if the remote end failed at any point, deal with 4567 // it by ignoring the rest of the restore on it 4568 if (!agentSuccess) { 4569 if (DEBUG) { 4570 Slog.i(TAG, "Agent failure; ending restore"); 4571 } 4572 mBackupHandler.removeMessages(MSG_TIMEOUT); 4573 tearDownPipes(); 4574 tearDownAgent(mTargetApp); 4575 mAgent = null; 4576 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4577 4578 // If this was a single-package restore, we halt immediately 4579 // with an agent error under these circumstances 4580 if (mOnlyPackage != null) { 4581 setResult(RestoreEngine.TARGET_FAILURE); 4582 setRunning(false); 4583 return false; 4584 } 4585 } 4586 } 4587 4588 // Problems setting up the agent communication, an explicitly 4589 // dropped file, or an already-ignored package: skip to the 4590 // next stream entry by reading and discarding this file. 4591 if (!okay) { 4592 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 4593 long bytesToConsume = (info.size + 511) & ~511; 4594 while (bytesToConsume > 0) { 4595 int toRead = (bytesToConsume > mBuffer.length) 4596 ? mBuffer.length : (int)bytesToConsume; 4597 long nRead = instream.read(mBuffer, 0, toRead); 4598 if (nRead >= 0) mBytes += nRead; 4599 if (nRead <= 0) break; 4600 bytesToConsume -= nRead; 4601 } 4602 } 4603 } 4604 } 4605 } catch (IOException e) { 4606 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 4607 setResult(RestoreEngine.TRANSPORT_FAILURE); 4608 info = null; 4609 } 4610 4611 // If we got here we're either running smoothly or we've finished 4612 if (info == null) { 4613 if (MORE_DEBUG) { 4614 Slog.i(TAG, "No [more] data for this package; tearing down"); 4615 } 4616 tearDownPipes(); 4617 tearDownAgent(mTargetApp); 4618 setRunning(false); 4619 } 4620 return (info != null); 4621 } 4622 4623 void setUpPipes() throws IOException { 4624 mPipes = ParcelFileDescriptor.createPipe(); 4625 } 4626 4627 void tearDownPipes() { 4628 if (mPipes != null) { 4629 try { 4630 mPipes[0].close(); 4631 mPipes[0] = null; 4632 mPipes[1].close(); 4633 mPipes[1] = null; 4634 } catch (IOException e) { 4635 Slog.w(TAG, "Couldn't close agent pipes", e); 4636 } 4637 mPipes = null; 4638 } 4639 } 4640 4641 void tearDownAgent(ApplicationInfo app) { 4642 if (mAgent != null) { 4643 try { 4644 // unbind and tidy up even on timeout or failure, just in case 4645 mActivityManager.unbindBackupAgent(app); 4646 4647 // The agent was running with a stub Application object, so shut it down. 4648 // !!! We hardcode the confirmation UI's package name here rather than use a 4649 // manifest flag! TODO something less direct. 4650 if (app.uid != Process.SYSTEM_UID 4651 && !app.packageName.equals("com.android.backupconfirm")) { 4652 if (DEBUG) Slog.d(TAG, "Killing host process"); 4653 mActivityManager.killApplicationProcess(app.processName, app.uid); 4654 } else { 4655 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 4656 } 4657 } catch (RemoteException e) { 4658 Slog.d(TAG, "Lost app trying to shut down"); 4659 } 4660 mAgent = null; 4661 } 4662 } 4663 4664 class RestoreInstallObserver extends IPackageInstallObserver.Stub { 4665 final AtomicBoolean mDone = new AtomicBoolean(); 4666 String mPackageName; 4667 int mResult; 4668 4669 public void reset() { 4670 synchronized (mDone) { 4671 mDone.set(false); 4672 } 4673 } 4674 4675 public void waitForCompletion() { 4676 synchronized (mDone) { 4677 while (mDone.get() == false) { 4678 try { 4679 mDone.wait(); 4680 } catch (InterruptedException e) { } 4681 } 4682 } 4683 } 4684 4685 int getResult() { 4686 return mResult; 4687 } 4688 4689 @Override 4690 public void packageInstalled(String packageName, int returnCode) 4691 throws RemoteException { 4692 synchronized (mDone) { 4693 mResult = returnCode; 4694 mPackageName = packageName; 4695 mDone.set(true); 4696 mDone.notifyAll(); 4697 } 4698 } 4699 } 4700 4701 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 4702 final AtomicBoolean mDone = new AtomicBoolean(); 4703 int mResult; 4704 4705 public void reset() { 4706 synchronized (mDone) { 4707 mDone.set(false); 4708 } 4709 } 4710 4711 public void waitForCompletion() { 4712 synchronized (mDone) { 4713 while (mDone.get() == false) { 4714 try { 4715 mDone.wait(); 4716 } catch (InterruptedException e) { } 4717 } 4718 } 4719 } 4720 4721 @Override 4722 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 4723 synchronized (mDone) { 4724 mResult = returnCode; 4725 mDone.set(true); 4726 mDone.notifyAll(); 4727 } 4728 } 4729 } 4730 4731 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 4732 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 4733 4734 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 4735 boolean okay = true; 4736 4737 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 4738 4739 // The file content is an .apk file. Copy it out to a staging location and 4740 // attempt to install it. 4741 File apkFile = new File(mDataDir, info.packageName); 4742 try { 4743 FileOutputStream apkStream = new FileOutputStream(apkFile); 4744 byte[] buffer = new byte[32 * 1024]; 4745 long size = info.size; 4746 while (size > 0) { 4747 long toRead = (buffer.length < size) ? buffer.length : size; 4748 int didRead = instream.read(buffer, 0, (int)toRead); 4749 if (didRead >= 0) mBytes += didRead; 4750 apkStream.write(buffer, 0, didRead); 4751 size -= didRead; 4752 } 4753 apkStream.close(); 4754 4755 // make sure the installer can read it 4756 apkFile.setReadable(true, false); 4757 4758 // Now install it 4759 Uri packageUri = Uri.fromFile(apkFile); 4760 mInstallObserver.reset(); 4761 mPackageManager.installPackage(packageUri, mInstallObserver, 4762 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 4763 installerPackage); 4764 mInstallObserver.waitForCompletion(); 4765 4766 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 4767 // The only time we continue to accept install of data even if the 4768 // apk install failed is if we had already determined that we could 4769 // accept the data regardless. 4770 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 4771 okay = false; 4772 } 4773 } else { 4774 // Okay, the install succeeded. Make sure it was the right app. 4775 boolean uninstall = false; 4776 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 4777 Slog.w(TAG, "Restore stream claimed to include apk for " 4778 + info.packageName + " but apk was really " 4779 + mInstallObserver.mPackageName); 4780 // delete the package we just put in place; it might be fraudulent 4781 okay = false; 4782 uninstall = true; 4783 } else { 4784 try { 4785 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 4786 PackageManager.GET_SIGNATURES); 4787 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 4788 Slog.w(TAG, "Restore stream contains apk of package " 4789 + info.packageName + " but it disallows backup/restore"); 4790 okay = false; 4791 } else { 4792 // So far so good -- do the signatures match the manifest? 4793 Signature[] sigs = mManifestSignatures.get(info.packageName); 4794 if (signaturesMatch(sigs, pkg)) { 4795 // If this is a system-uid app without a declared backup agent, 4796 // don't restore any of the file data. 4797 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 4798 && (pkg.applicationInfo.backupAgentName == null)) { 4799 Slog.w(TAG, "Installed app " + info.packageName 4800 + " has restricted uid and no agent"); 4801 okay = false; 4802 } 4803 } else { 4804 Slog.w(TAG, "Installed app " + info.packageName 4805 + " signatures do not match restore manifest"); 4806 okay = false; 4807 uninstall = true; 4808 } 4809 } 4810 } catch (NameNotFoundException e) { 4811 Slog.w(TAG, "Install of package " + info.packageName 4812 + " succeeded but now not found"); 4813 okay = false; 4814 } 4815 } 4816 4817 // If we're not okay at this point, we need to delete the package 4818 // that we just installed. 4819 if (uninstall) { 4820 mDeleteObserver.reset(); 4821 mPackageManager.deletePackage(mInstallObserver.mPackageName, 4822 mDeleteObserver, 0); 4823 mDeleteObserver.waitForCompletion(); 4824 } 4825 } 4826 } catch (IOException e) { 4827 Slog.e(TAG, "Unable to transcribe restored apk for install"); 4828 okay = false; 4829 } finally { 4830 apkFile.delete(); 4831 } 4832 4833 return okay; 4834 } 4835 4836 // Given an actual file content size, consume the post-content padding mandated 4837 // by the tar format. 4838 void skipTarPadding(long size, InputStream instream) throws IOException { 4839 long partial = (size + 512) % 512; 4840 if (partial > 0) { 4841 final int needed = 512 - (int)partial; 4842 if (MORE_DEBUG) { 4843 Slog.i(TAG, "Skipping tar padding: " + needed + " bytes"); 4844 } 4845 byte[] buffer = new byte[needed]; 4846 if (readExactly(instream, buffer, 0, needed) == needed) { 4847 mBytes += needed; 4848 } else throw new IOException("Unexpected EOF in padding"); 4849 } 4850 } 4851 4852 // Read a widget metadata file, returning the restored blob 4853 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 4854 // Fail on suspiciously large widget dump files 4855 if (info.size > 64 * 1024) { 4856 throw new IOException("Metadata too big; corrupt? size=" + info.size); 4857 } 4858 4859 byte[] buffer = new byte[(int) info.size]; 4860 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 4861 mBytes += info.size; 4862 } else throw new IOException("Unexpected EOF in widget data"); 4863 4864 String[] str = new String[1]; 4865 int offset = extractLine(buffer, 0, str); 4866 int version = Integer.parseInt(str[0]); 4867 if (version == BACKUP_MANIFEST_VERSION) { 4868 offset = extractLine(buffer, offset, str); 4869 final String pkg = str[0]; 4870 if (info.packageName.equals(pkg)) { 4871 // Data checks out -- the rest of the buffer is a concatenation of 4872 // binary blobs as described in the comment at writeAppWidgetData() 4873 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 4874 offset, buffer.length - offset); 4875 DataInputStream in = new DataInputStream(bin); 4876 while (bin.available() > 0) { 4877 int token = in.readInt(); 4878 int size = in.readInt(); 4879 if (size > 64 * 1024) { 4880 throw new IOException("Datum " 4881 + Integer.toHexString(token) 4882 + " too big; corrupt? size=" + info.size); 4883 } 4884 switch (token) { 4885 case BACKUP_WIDGET_METADATA_TOKEN: 4886 { 4887 if (MORE_DEBUG) { 4888 Slog.i(TAG, "Got widget metadata for " + info.packageName); 4889 } 4890 mWidgetData = new byte[size]; 4891 in.read(mWidgetData); 4892 break; 4893 } 4894 default: 4895 { 4896 if (DEBUG) { 4897 Slog.i(TAG, "Ignoring metadata blob " 4898 + Integer.toHexString(token) 4899 + " for " + info.packageName); 4900 } 4901 in.skipBytes(size); 4902 break; 4903 } 4904 } 4905 } 4906 } else { 4907 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 4908 + " but widget data for " + pkg); 4909 } 4910 } else { 4911 Slog.w(TAG, "Unsupported metadata version " + version); 4912 } 4913 } 4914 4915 // Returns a policy constant 4916 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 4917 throws IOException { 4918 // Fail on suspiciously large manifest files 4919 if (info.size > 64 * 1024) { 4920 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 4921 } 4922 4923 byte[] buffer = new byte[(int) info.size]; 4924 if (MORE_DEBUG) { 4925 Slog.i(TAG, " readAppManifest() looking for " + info.size + " bytes, " 4926 + mBytes + " already consumed"); 4927 } 4928 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 4929 mBytes += info.size; 4930 } else throw new IOException("Unexpected EOF in manifest"); 4931 4932 RestorePolicy policy = RestorePolicy.IGNORE; 4933 String[] str = new String[1]; 4934 int offset = 0; 4935 4936 try { 4937 offset = extractLine(buffer, offset, str); 4938 int version = Integer.parseInt(str[0]); 4939 if (version == BACKUP_MANIFEST_VERSION) { 4940 offset = extractLine(buffer, offset, str); 4941 String manifestPackage = str[0]; 4942 // TODO: handle <original-package> 4943 if (manifestPackage.equals(info.packageName)) { 4944 offset = extractLine(buffer, offset, str); 4945 version = Integer.parseInt(str[0]); // app version 4946 offset = extractLine(buffer, offset, str); 4947 int platformVersion = Integer.parseInt(str[0]); 4948 offset = extractLine(buffer, offset, str); 4949 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 4950 offset = extractLine(buffer, offset, str); 4951 boolean hasApk = str[0].equals("1"); 4952 offset = extractLine(buffer, offset, str); 4953 int numSigs = Integer.parseInt(str[0]); 4954 if (numSigs > 0) { 4955 Signature[] sigs = new Signature[numSigs]; 4956 for (int i = 0; i < numSigs; i++) { 4957 offset = extractLine(buffer, offset, str); 4958 sigs[i] = new Signature(str[0]); 4959 } 4960 mManifestSignatures.put(info.packageName, sigs); 4961 4962 // Okay, got the manifest info we need... 4963 try { 4964 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 4965 info.packageName, PackageManager.GET_SIGNATURES); 4966 // Fall through to IGNORE if the app explicitly disallows backup 4967 final int flags = pkgInfo.applicationInfo.flags; 4968 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 4969 // Restore system-uid-space packages only if they have 4970 // defined a custom backup agent 4971 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 4972 || (pkgInfo.applicationInfo.backupAgentName != null)) { 4973 // Verify signatures against any installed version; if they 4974 // don't match, then we fall though and ignore the data. The 4975 // signatureMatch() method explicitly ignores the signature 4976 // check for packages installed on the system partition, because 4977 // such packages are signed with the platform cert instead of 4978 // the app developer's cert, so they're different on every 4979 // device. 4980 if (signaturesMatch(sigs, pkgInfo)) { 4981 if (pkgInfo.versionCode >= version) { 4982 Slog.i(TAG, "Sig + version match; taking data"); 4983 policy = RestorePolicy.ACCEPT; 4984 } else { 4985 // The data is from a newer version of the app than 4986 // is presently installed. That means we can only 4987 // use it if the matching apk is also supplied. 4988 if (mAllowApks) { 4989 Slog.i(TAG, "Data version " + version 4990 + " is newer than installed version " 4991 + pkgInfo.versionCode 4992 + " - requiring apk"); 4993 policy = RestorePolicy.ACCEPT_IF_APK; 4994 } else { 4995 Slog.i(TAG, "Data requires newer version " 4996 + version + "; ignoring"); 4997 policy = RestorePolicy.IGNORE; 4998 } 4999 } 5000 } else { 5001 Slog.w(TAG, "Restore manifest signatures do not match " 5002 + "installed application for " + info.packageName); 5003 } 5004 } else { 5005 Slog.w(TAG, "Package " + info.packageName 5006 + " is system level with no agent"); 5007 } 5008 } else { 5009 if (DEBUG) Slog.i(TAG, "Restore manifest from " 5010 + info.packageName + " but allowBackup=false"); 5011 } 5012 } catch (NameNotFoundException e) { 5013 // Okay, the target app isn't installed. We can process 5014 // the restore properly only if the dataset provides the 5015 // apk file and we can successfully install it. 5016 if (mAllowApks) { 5017 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 5018 + " not installed; requiring apk in dataset"); 5019 policy = RestorePolicy.ACCEPT_IF_APK; 5020 } else { 5021 policy = RestorePolicy.IGNORE; 5022 } 5023 } 5024 5025 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 5026 Slog.i(TAG, "Cannot restore package " + info.packageName 5027 + " without the matching .apk"); 5028 } 5029 } else { 5030 Slog.i(TAG, "Missing signature on backed-up package " 5031 + info.packageName); 5032 } 5033 } else { 5034 Slog.i(TAG, "Expected package " + info.packageName 5035 + " but restore manifest claims " + manifestPackage); 5036 } 5037 } else { 5038 Slog.i(TAG, "Unknown restore manifest version " + version 5039 + " for package " + info.packageName); 5040 } 5041 } catch (NumberFormatException e) { 5042 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 5043 } catch (IllegalArgumentException e) { 5044 Slog.w(TAG, e.getMessage()); 5045 } 5046 5047 return policy; 5048 } 5049 5050 // Builds a line from a byte buffer starting at 'offset', and returns 5051 // the index of the next unconsumed data in the buffer. 5052 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 5053 final int end = buffer.length; 5054 if (offset >= end) throw new IOException("Incomplete data"); 5055 5056 int pos; 5057 for (pos = offset; pos < end; pos++) { 5058 byte c = buffer[pos]; 5059 // at LF we declare end of line, and return the next char as the 5060 // starting point for the next time through 5061 if (c == '\n') { 5062 break; 5063 } 5064 } 5065 outStr[0] = new String(buffer, offset, pos - offset); 5066 pos++; // may be pointing an extra byte past the end but that's okay 5067 return pos; 5068 } 5069 5070 void dumpFileMetadata(FileMetadata info) { 5071 if (DEBUG) { 5072 StringBuilder b = new StringBuilder(128); 5073 5074 // mode string 5075 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 5076 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 5077 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 5078 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 5079 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 5080 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 5081 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 5082 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 5083 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 5084 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 5085 b.append(String.format(" %9d ", info.size)); 5086 5087 Date stamp = new Date(info.mtime); 5088 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 5089 5090 b.append(info.packageName); 5091 b.append(" :: "); 5092 b.append(info.domain); 5093 b.append(" :: "); 5094 b.append(info.path); 5095 5096 Slog.i(TAG, b.toString()); 5097 } 5098 } 5099 5100 // Consume a tar file header block [sequence] and accumulate the relevant metadata 5101 FileMetadata readTarHeaders(InputStream instream) throws IOException { 5102 byte[] block = new byte[512]; 5103 FileMetadata info = null; 5104 5105 boolean gotHeader = readTarHeader(instream, block); 5106 if (gotHeader) { 5107 try { 5108 // okay, presume we're okay, and extract the various metadata 5109 info = new FileMetadata(); 5110 info.size = extractRadix(block, 124, 12, 8); 5111 info.mtime = extractRadix(block, 136, 12, 8); 5112 info.mode = extractRadix(block, 100, 8, 8); 5113 5114 info.path = extractString(block, 345, 155); // prefix 5115 String path = extractString(block, 0, 100); 5116 if (path.length() > 0) { 5117 if (info.path.length() > 0) info.path += '/'; 5118 info.path += path; 5119 } 5120 5121 // tar link indicator field: 1 byte at offset 156 in the header. 5122 int typeChar = block[156]; 5123 if (typeChar == 'x') { 5124 // pax extended header, so we need to read that 5125 gotHeader = readPaxExtendedHeader(instream, info); 5126 if (gotHeader) { 5127 // and after a pax extended header comes another real header -- read 5128 // that to find the real file type 5129 gotHeader = readTarHeader(instream, block); 5130 } 5131 if (!gotHeader) throw new IOException("Bad or missing pax header"); 5132 5133 typeChar = block[156]; 5134 } 5135 5136 switch (typeChar) { 5137 case '0': info.type = BackupAgent.TYPE_FILE; break; 5138 case '5': { 5139 info.type = BackupAgent.TYPE_DIRECTORY; 5140 if (info.size != 0) { 5141 Slog.w(TAG, "Directory entry with nonzero size in header"); 5142 info.size = 0; 5143 } 5144 break; 5145 } 5146 case 0: { 5147 // presume EOF 5148 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 5149 return null; 5150 } 5151 default: { 5152 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 5153 throw new IOException("Unknown entity type " + typeChar); 5154 } 5155 } 5156 5157 // Parse out the path 5158 // 5159 // first: apps/shared/unrecognized 5160 if (FullBackup.SHARED_PREFIX.regionMatches(0, 5161 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 5162 // File in shared storage. !!! TODO: implement this. 5163 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 5164 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 5165 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 5166 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 5167 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 5168 info.path, 0, FullBackup.APPS_PREFIX.length())) { 5169 // App content! Parse out the package name and domain 5170 5171 // strip the apps/ prefix 5172 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 5173 5174 // extract the package name 5175 int slash = info.path.indexOf('/'); 5176 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 5177 info.packageName = info.path.substring(0, slash); 5178 info.path = info.path.substring(slash+1); 5179 5180 // if it's a manifest or metadata payload we're done, otherwise parse 5181 // out the domain into which the file will be restored 5182 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 5183 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 5184 slash = info.path.indexOf('/'); 5185 if (slash < 0) { 5186 throw new IOException("Illegal semantic path in non-manifest " 5187 + info.path); 5188 } 5189 info.domain = info.path.substring(0, slash); 5190 info.path = info.path.substring(slash + 1); 5191 } 5192 } 5193 } catch (IOException e) { 5194 if (DEBUG) { 5195 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 5196 HEXLOG(block); 5197 } 5198 throw e; 5199 } 5200 } 5201 return info; 5202 } 5203 5204 private boolean isRestorableFile(FileMetadata info) { 5205 if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { 5206 if (MORE_DEBUG) { 5207 Slog.i(TAG, "Dropping cache file path " + info.path); 5208 } 5209 return false; 5210 } 5211 5212 if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) { 5213 // It's possible this is "no-backup" dir contents in an archive stream 5214 // produced on a device running a version of the OS that predates that 5215 // API. Respect the no-backup intention and don't let the data get to 5216 // the app. 5217 if (info.path.startsWith("no_backup/")) { 5218 if (MORE_DEBUG) { 5219 Slog.i(TAG, "Dropping no_backup file path " + info.path); 5220 } 5221 return false; 5222 } 5223 } 5224 5225 // The path needs to be canonical 5226 if (info.path.contains("..") || info.path.contains("//")) { 5227 if (MORE_DEBUG) { 5228 Slog.w(TAG, "Dropping invalid path " + info.path); 5229 } 5230 return false; 5231 } 5232 5233 // Otherwise we think this file is good to go 5234 return true; 5235 } 5236 5237 private void HEXLOG(byte[] block) { 5238 int offset = 0; 5239 int todo = block.length; 5240 StringBuilder buf = new StringBuilder(64); 5241 while (todo > 0) { 5242 buf.append(String.format("%04x ", offset)); 5243 int numThisLine = (todo > 16) ? 16 : todo; 5244 for (int i = 0; i < numThisLine; i++) { 5245 buf.append(String.format("%02x ", block[offset+i])); 5246 } 5247 Slog.i("hexdump", buf.toString()); 5248 buf.setLength(0); 5249 todo -= numThisLine; 5250 offset += numThisLine; 5251 } 5252 } 5253 5254 // Read exactly the given number of bytes into a buffer at the stated offset. 5255 // Returns false if EOF is encountered before the requested number of bytes 5256 // could be read. 5257 int readExactly(InputStream in, byte[] buffer, int offset, int size) 5258 throws IOException { 5259 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 5260if (MORE_DEBUG) Slog.i(TAG, " ... readExactly(" + size + ") called"); 5261 int soFar = 0; 5262 while (soFar < size) { 5263 int nRead = in.read(buffer, offset + soFar, size - soFar); 5264 if (nRead <= 0) { 5265 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 5266 break; 5267 } 5268 soFar += nRead; 5269if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar)); 5270 } 5271 return soFar; 5272 } 5273 5274 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 5275 final int got = readExactly(instream, block, 0, 512); 5276 if (got == 0) return false; // Clean EOF 5277 if (got < 512) throw new IOException("Unable to read full block header"); 5278 mBytes += 512; 5279 return true; 5280 } 5281 5282 // overwrites 'info' fields based on the pax extended header 5283 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 5284 throws IOException { 5285 // We should never see a pax extended header larger than this 5286 if (info.size > 32*1024) { 5287 Slog.w(TAG, "Suspiciously large pax header size " + info.size 5288 + " - aborting"); 5289 throw new IOException("Sanity failure: pax header size " + info.size); 5290 } 5291 5292 // read whole blocks, not just the content size 5293 int numBlocks = (int)((info.size + 511) >> 9); 5294 byte[] data = new byte[numBlocks * 512]; 5295 if (readExactly(instream, data, 0, data.length) < data.length) { 5296 throw new IOException("Unable to read full pax header"); 5297 } 5298 mBytes += data.length; 5299 5300 final int contentSize = (int) info.size; 5301 int offset = 0; 5302 do { 5303 // extract the line at 'offset' 5304 int eol = offset+1; 5305 while (eol < contentSize && data[eol] != ' ') eol++; 5306 if (eol >= contentSize) { 5307 // error: we just hit EOD looking for the end of the size field 5308 throw new IOException("Invalid pax data"); 5309 } 5310 // eol points to the space between the count and the key 5311 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 5312 int key = eol + 1; // start of key=value 5313 eol = offset + linelen - 1; // trailing LF 5314 int value; 5315 for (value = key+1; data[value] != '=' && value <= eol; value++); 5316 if (value > eol) { 5317 throw new IOException("Invalid pax declaration"); 5318 } 5319 5320 // pax requires that key/value strings be in UTF-8 5321 String keyStr = new String(data, key, value-key, "UTF-8"); 5322 // -1 to strip the trailing LF 5323 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 5324 5325 if ("path".equals(keyStr)) { 5326 info.path = valStr; 5327 } else if ("size".equals(keyStr)) { 5328 info.size = Long.parseLong(valStr); 5329 } else { 5330 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 5331 } 5332 5333 offset += linelen; 5334 } while (offset < contentSize); 5335 5336 return true; 5337 } 5338 5339 long extractRadix(byte[] data, int offset, int maxChars, int radix) 5340 throws IOException { 5341 long value = 0; 5342 final int end = offset + maxChars; 5343 for (int i = offset; i < end; i++) { 5344 final byte b = data[i]; 5345 // Numeric fields in tar can terminate with either NUL or SPC 5346 if (b == 0 || b == ' ') break; 5347 if (b < '0' || b > ('0' + radix - 1)) { 5348 throw new IOException("Invalid number in header: '" + (char)b 5349 + "' for radix " + radix); 5350 } 5351 value = radix * value + (b - '0'); 5352 } 5353 return value; 5354 } 5355 5356 String extractString(byte[] data, int offset, int maxChars) throws IOException { 5357 final int end = offset + maxChars; 5358 int eos = offset; 5359 // tar string fields terminate early with a NUL 5360 while (eos < end && data[eos] != 0) eos++; 5361 return new String(data, offset, eos-offset, "US-ASCII"); 5362 } 5363 5364 void sendStartRestore() { 5365 if (mObserver != null) { 5366 try { 5367 mObserver.onStartRestore(); 5368 } catch (RemoteException e) { 5369 Slog.w(TAG, "full restore observer went away: startRestore"); 5370 mObserver = null; 5371 } 5372 } 5373 } 5374 5375 void sendOnRestorePackage(String name) { 5376 if (mObserver != null) { 5377 try { 5378 // TODO: use a more user-friendly name string 5379 mObserver.onRestorePackage(name); 5380 } catch (RemoteException e) { 5381 Slog.w(TAG, "full restore observer went away: restorePackage"); 5382 mObserver = null; 5383 } 5384 } 5385 } 5386 5387 void sendEndRestore() { 5388 if (mObserver != null) { 5389 try { 5390 mObserver.onEndRestore(); 5391 } catch (RemoteException e) { 5392 Slog.w(TAG, "full restore observer went away: endRestore"); 5393 mObserver = null; 5394 } 5395 } 5396 } 5397 } 5398 5399 // ***** end new engine class *** 5400 5401 class PerformAdbRestoreTask implements Runnable { 5402 ParcelFileDescriptor mInputFile; 5403 String mCurrentPassword; 5404 String mDecryptPassword; 5405 IFullBackupRestoreObserver mObserver; 5406 AtomicBoolean mLatchObject; 5407 IBackupAgent mAgent; 5408 String mAgentPackage; 5409 ApplicationInfo mTargetApp; 5410 FullBackupObbConnection mObbConnection = null; 5411 ParcelFileDescriptor[] mPipes = null; 5412 byte[] mWidgetData = null; 5413 5414 long mBytes; 5415 5416 // possible handling states for a given package in the restore dataset 5417 final HashMap<String, RestorePolicy> mPackagePolicies 5418 = new HashMap<String, RestorePolicy>(); 5419 5420 // installer package names for each encountered app, derived from the manifests 5421 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 5422 5423 // Signatures for a given package found in its manifest file 5424 final HashMap<String, Signature[]> mManifestSignatures 5425 = new HashMap<String, Signature[]>(); 5426 5427 // Packages we've already wiped data on when restoring their first file 5428 final HashSet<String> mClearedPackages = new HashSet<String>(); 5429 5430 PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword, 5431 IFullBackupRestoreObserver observer, AtomicBoolean latch) { 5432 mInputFile = fd; 5433 mCurrentPassword = curPassword; 5434 mDecryptPassword = decryptPassword; 5435 mObserver = observer; 5436 mLatchObject = latch; 5437 mAgent = null; 5438 mAgentPackage = null; 5439 mTargetApp = null; 5440 mObbConnection = new FullBackupObbConnection(); 5441 5442 // Which packages we've already wiped data on. We prepopulate this 5443 // with a whitelist of packages known to be unclearable. 5444 mClearedPackages.add("android"); 5445 mClearedPackages.add(SETTINGS_PACKAGE); 5446 } 5447 5448 class RestoreFileRunnable implements Runnable { 5449 IBackupAgent mAgent; 5450 FileMetadata mInfo; 5451 ParcelFileDescriptor mSocket; 5452 int mToken; 5453 5454 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 5455 ParcelFileDescriptor socket, int token) throws IOException { 5456 mAgent = agent; 5457 mInfo = info; 5458 mToken = token; 5459 5460 // This class is used strictly for process-local binder invocations. The 5461 // semantics of ParcelFileDescriptor differ in this case; in particular, we 5462 // do not automatically get a 'dup'ed descriptor that we can can continue 5463 // to use asynchronously from the caller. So, we make sure to dup it ourselves 5464 // before proceeding to do the restore. 5465 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 5466 } 5467 5468 @Override 5469 public void run() { 5470 try { 5471 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 5472 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 5473 mToken, mBackupManagerBinder); 5474 } catch (RemoteException e) { 5475 // never happens; this is used strictly for local binder calls 5476 } 5477 } 5478 } 5479 5480 @Override 5481 public void run() { 5482 Slog.i(TAG, "--- Performing full-dataset restore ---"); 5483 mObbConnection.establish(); 5484 sendStartRestore(); 5485 5486 // Are we able to restore shared-storage data? 5487 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 5488 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT); 5489 } 5490 5491 FileInputStream rawInStream = null; 5492 DataInputStream rawDataIn = null; 5493 try { 5494 if (!backupPasswordMatches(mCurrentPassword)) { 5495 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 5496 return; 5497 } 5498 5499 mBytes = 0; 5500 byte[] buffer = new byte[32 * 1024]; 5501 rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); 5502 rawDataIn = new DataInputStream(rawInStream); 5503 5504 // First, parse out the unencrypted/uncompressed header 5505 boolean compressed = false; 5506 InputStream preCompressStream = rawInStream; 5507 final InputStream in; 5508 5509 boolean okay = false; 5510 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length(); 5511 byte[] streamHeader = new byte[headerLen]; 5512 rawDataIn.readFully(streamHeader); 5513 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8"); 5514 if (Arrays.equals(magicBytes, streamHeader)) { 5515 // okay, header looks good. now parse out the rest of the fields. 5516 String s = readHeaderLine(rawInStream); 5517 final int archiveVersion = Integer.parseInt(s); 5518 if (archiveVersion <= BACKUP_FILE_VERSION) { 5519 // okay, it's a version we recognize. if it's version 1, we may need 5520 // to try two different PBKDF2 regimes to compare checksums. 5521 final boolean pbkdf2Fallback = (archiveVersion == 1); 5522 5523 s = readHeaderLine(rawInStream); 5524 compressed = (Integer.parseInt(s) != 0); 5525 s = readHeaderLine(rawInStream); 5526 if (s.equals("none")) { 5527 // no more header to parse; we're good to go 5528 okay = true; 5529 } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) { 5530 preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback, 5531 rawInStream); 5532 if (preCompressStream != null) { 5533 okay = true; 5534 } 5535 } else Slog.w(TAG, "Archive is encrypted but no password given"); 5536 } else Slog.w(TAG, "Wrong header version: " + s); 5537 } else Slog.w(TAG, "Didn't read the right header magic"); 5538 5539 if (!okay) { 5540 Slog.w(TAG, "Invalid restore data; aborting."); 5541 return; 5542 } 5543 5544 // okay, use the right stream layer based on compression 5545 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream; 5546 5547 boolean didRestore; 5548 do { 5549 didRestore = restoreOneFile(in, buffer); 5550 } while (didRestore); 5551 5552 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); 5553 } catch (IOException e) { 5554 Slog.e(TAG, "Unable to read restore input"); 5555 } finally { 5556 tearDownPipes(); 5557 tearDownAgent(mTargetApp); 5558 5559 try { 5560 if (rawDataIn != null) rawDataIn.close(); 5561 if (rawInStream != null) rawInStream.close(); 5562 mInputFile.close(); 5563 } catch (IOException e) { 5564 Slog.w(TAG, "Close of restore data pipe threw", e); 5565 /* nothing we can do about this */ 5566 } 5567 synchronized (mCurrentOpLock) { 5568 mCurrentOperations.clear(); 5569 } 5570 synchronized (mLatchObject) { 5571 mLatchObject.set(true); 5572 mLatchObject.notifyAll(); 5573 } 5574 mObbConnection.tearDown(); 5575 sendEndRestore(); 5576 Slog.d(TAG, "Full restore pass complete."); 5577 mWakelock.release(); 5578 } 5579 } 5580 5581 String readHeaderLine(InputStream in) throws IOException { 5582 int c; 5583 StringBuilder buffer = new StringBuilder(80); 5584 while ((c = in.read()) >= 0) { 5585 if (c == '\n') break; // consume and discard the newlines 5586 buffer.append((char)c); 5587 } 5588 return buffer.toString(); 5589 } 5590 5591 InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt, 5592 int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream, 5593 boolean doLog) { 5594 InputStream result = null; 5595 5596 try { 5597 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 5598 SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt, 5599 rounds); 5600 byte[] IV = hexToByteArray(userIvHex); 5601 IvParameterSpec ivSpec = new IvParameterSpec(IV); 5602 c.init(Cipher.DECRYPT_MODE, 5603 new SecretKeySpec(userKey.getEncoded(), "AES"), 5604 ivSpec); 5605 byte[] mkCipher = hexToByteArray(masterKeyBlobHex); 5606 byte[] mkBlob = c.doFinal(mkCipher); 5607 5608 // first, the master key IV 5609 int offset = 0; 5610 int len = mkBlob[offset++]; 5611 IV = Arrays.copyOfRange(mkBlob, offset, offset + len); 5612 offset += len; 5613 // then the master key itself 5614 len = mkBlob[offset++]; 5615 byte[] mk = Arrays.copyOfRange(mkBlob, 5616 offset, offset + len); 5617 offset += len; 5618 // and finally the master key checksum hash 5619 len = mkBlob[offset++]; 5620 byte[] mkChecksum = Arrays.copyOfRange(mkBlob, 5621 offset, offset + len); 5622 5623 // now validate the decrypted master key against the checksum 5624 byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds); 5625 if (Arrays.equals(calculatedCk, mkChecksum)) { 5626 ivSpec = new IvParameterSpec(IV); 5627 c.init(Cipher.DECRYPT_MODE, 5628 new SecretKeySpec(mk, "AES"), 5629 ivSpec); 5630 // Only if all of the above worked properly will 'result' be assigned 5631 result = new CipherInputStream(rawInStream, c); 5632 } else if (doLog) Slog.w(TAG, "Incorrect password"); 5633 } catch (InvalidAlgorithmParameterException e) { 5634 if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e); 5635 } catch (BadPaddingException e) { 5636 // This case frequently occurs when the wrong password is used to decrypt 5637 // the master key. Use the identical "incorrect password" log text as is 5638 // used in the checksum failure log in order to avoid providing additional 5639 // information to an attacker. 5640 if (doLog) Slog.w(TAG, "Incorrect password"); 5641 } catch (IllegalBlockSizeException e) { 5642 if (doLog) Slog.w(TAG, "Invalid block size in master key"); 5643 } catch (NoSuchAlgorithmException e) { 5644 if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!"); 5645 } catch (NoSuchPaddingException e) { 5646 if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!"); 5647 } catch (InvalidKeyException e) { 5648 if (doLog) Slog.w(TAG, "Illegal password; aborting"); 5649 } 5650 5651 return result; 5652 } 5653 5654 InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback, 5655 InputStream rawInStream) { 5656 InputStream result = null; 5657 try { 5658 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) { 5659 5660 String userSaltHex = readHeaderLine(rawInStream); // 5 5661 byte[] userSalt = hexToByteArray(userSaltHex); 5662 5663 String ckSaltHex = readHeaderLine(rawInStream); // 6 5664 byte[] ckSalt = hexToByteArray(ckSaltHex); 5665 5666 int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 5667 String userIvHex = readHeaderLine(rawInStream); // 8 5668 5669 String masterKeyBlobHex = readHeaderLine(rawInStream); // 9 5670 5671 // decrypt the master key blob 5672 result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt, 5673 rounds, userIvHex, masterKeyBlobHex, rawInStream, false); 5674 if (result == null && pbkdf2Fallback) { 5675 result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt, 5676 rounds, userIvHex, masterKeyBlobHex, rawInStream, true); 5677 } 5678 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName); 5679 } catch (NumberFormatException e) { 5680 Slog.w(TAG, "Can't parse restore data header"); 5681 } catch (IOException e) { 5682 Slog.w(TAG, "Can't read input header"); 5683 } 5684 5685 return result; 5686 } 5687 5688 boolean restoreOneFile(InputStream instream, byte[] buffer) { 5689 FileMetadata info; 5690 try { 5691 info = readTarHeaders(instream); 5692 if (info != null) { 5693 if (MORE_DEBUG) { 5694 dumpFileMetadata(info); 5695 } 5696 5697 final String pkg = info.packageName; 5698 if (!pkg.equals(mAgentPackage)) { 5699 // okay, change in package; set up our various 5700 // bookkeeping if we haven't seen it yet 5701 if (!mPackagePolicies.containsKey(pkg)) { 5702 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5703 } 5704 5705 // Clean up the previous agent relationship if necessary, 5706 // and let the observer know we're considering a new app. 5707 if (mAgent != null) { 5708 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 5709 // Now we're really done 5710 tearDownPipes(); 5711 tearDownAgent(mTargetApp); 5712 mTargetApp = null; 5713 mAgentPackage = null; 5714 } 5715 } 5716 5717 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 5718 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 5719 mPackageInstallers.put(pkg, info.installerPackageName); 5720 // We've read only the manifest content itself at this point, 5721 // so consume the footer before looping around to the next 5722 // input file 5723 skipTarPadding(info.size, instream); 5724 sendOnRestorePackage(pkg); 5725 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 5726 // Metadata blobs! 5727 readMetadata(info, instream); 5728 skipTarPadding(info.size, instream); 5729 } else { 5730 // Non-manifest, so it's actual file data. Is this a package 5731 // we're ignoring? 5732 boolean okay = true; 5733 RestorePolicy policy = mPackagePolicies.get(pkg); 5734 switch (policy) { 5735 case IGNORE: 5736 okay = false; 5737 break; 5738 5739 case ACCEPT_IF_APK: 5740 // If we're in accept-if-apk state, then the first file we 5741 // see MUST be the apk. 5742 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5743 if (DEBUG) Slog.d(TAG, "APK file; installing"); 5744 // Try to install the app. 5745 String installerName = mPackageInstallers.get(pkg); 5746 okay = installApk(info, installerName, instream); 5747 // good to go; promote to ACCEPT 5748 mPackagePolicies.put(pkg, (okay) 5749 ? RestorePolicy.ACCEPT 5750 : RestorePolicy.IGNORE); 5751 // At this point we've consumed this file entry 5752 // ourselves, so just strip the tar footer and 5753 // go on to the next file in the input stream 5754 skipTarPadding(info.size, instream); 5755 return true; 5756 } else { 5757 // File data before (or without) the apk. We can't 5758 // handle it coherently in this case so ignore it. 5759 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5760 okay = false; 5761 } 5762 break; 5763 5764 case ACCEPT: 5765 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5766 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 5767 // we can take the data without the apk, so we 5768 // *want* to do so. skip the apk by declaring this 5769 // one file not-okay without changing the restore 5770 // policy for the package. 5771 okay = false; 5772 } 5773 break; 5774 5775 default: 5776 // Something has gone dreadfully wrong when determining 5777 // the restore policy from the manifest. Ignore the 5778 // rest of this package's data. 5779 Slog.e(TAG, "Invalid policy from manifest"); 5780 okay = false; 5781 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5782 break; 5783 } 5784 5785 // The path needs to be canonical 5786 if (info.path.contains("..") || info.path.contains("//")) { 5787 if (MORE_DEBUG) { 5788 Slog.w(TAG, "Dropping invalid path " + info.path); 5789 } 5790 okay = false; 5791 } 5792 5793 // If the policy is satisfied, go ahead and set up to pipe the 5794 // data to the agent. 5795 if (DEBUG && okay && mAgent != null) { 5796 Slog.i(TAG, "Reusing existing agent instance"); 5797 } 5798 if (okay && mAgent == null) { 5799 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 5800 5801 try { 5802 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 5803 5804 // If we haven't sent any data to this app yet, we probably 5805 // need to clear it first. Check that. 5806 if (!mClearedPackages.contains(pkg)) { 5807 // apps with their own backup agents are 5808 // responsible for coherently managing a full 5809 // restore. 5810 if (mTargetApp.backupAgentName == null) { 5811 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 5812 clearApplicationDataSynchronous(pkg); 5813 } else { 5814 if (DEBUG) Slog.d(TAG, "backup agent (" 5815 + mTargetApp.backupAgentName + ") => no clear"); 5816 } 5817 mClearedPackages.add(pkg); 5818 } else { 5819 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 5820 } 5821 5822 // All set; now set up the IPC and launch the agent 5823 setUpPipes(); 5824 mAgent = bindToAgentSynchronous(mTargetApp, 5825 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 5826 mAgentPackage = pkg; 5827 } catch (IOException e) { 5828 // fall through to error handling 5829 } catch (NameNotFoundException e) { 5830 // fall through to error handling 5831 } 5832 5833 if (mAgent == null) { 5834 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 5835 okay = false; 5836 tearDownPipes(); 5837 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5838 } 5839 } 5840 5841 // Sanity check: make sure we never give data to the wrong app. This 5842 // should never happen but a little paranoia here won't go amiss. 5843 if (okay && !pkg.equals(mAgentPackage)) { 5844 Slog.e(TAG, "Restoring data for " + pkg 5845 + " but agent is for " + mAgentPackage); 5846 okay = false; 5847 } 5848 5849 // At this point we have an agent ready to handle the full 5850 // restore data as well as a pipe for sending data to 5851 // that agent. Tell the agent to start reading from the 5852 // pipe. 5853 if (okay) { 5854 boolean agentSuccess = true; 5855 long toCopy = info.size; 5856 final int token = generateToken(); 5857 try { 5858 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 5859 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 5860 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 5861 + " : " + info.path); 5862 mObbConnection.restoreObbFile(pkg, mPipes[0], 5863 info.size, info.type, info.path, info.mode, 5864 info.mtime, token, mBackupManagerBinder); 5865 } else { 5866 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 5867 + info.path); 5868 // fire up the app's agent listening on the socket. If 5869 // the agent is running in the system process we can't 5870 // just invoke it asynchronously, so we provide a thread 5871 // for it here. 5872 if (mTargetApp.processName.equals("system")) { 5873 Slog.d(TAG, "system process agent - spinning a thread"); 5874 RestoreFileRunnable runner = new RestoreFileRunnable( 5875 mAgent, info, mPipes[0], token); 5876 new Thread(runner, "restore-sys-runner").start(); 5877 } else { 5878 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 5879 info.domain, info.path, info.mode, info.mtime, 5880 token, mBackupManagerBinder); 5881 } 5882 } 5883 } catch (IOException e) { 5884 // couldn't dup the socket for a process-local restore 5885 Slog.d(TAG, "Couldn't establish restore"); 5886 agentSuccess = false; 5887 okay = false; 5888 } catch (RemoteException e) { 5889 // whoops, remote entity went away. We'll eat the content 5890 // ourselves, then, and not copy it over. 5891 Slog.e(TAG, "Agent crashed during full restore"); 5892 agentSuccess = false; 5893 okay = false; 5894 } 5895 5896 // Copy over the data if the agent is still good 5897 if (okay) { 5898 boolean pipeOkay = true; 5899 FileOutputStream pipe = new FileOutputStream( 5900 mPipes[1].getFileDescriptor()); 5901 while (toCopy > 0) { 5902 int toRead = (toCopy > buffer.length) 5903 ? buffer.length : (int)toCopy; 5904 int nRead = instream.read(buffer, 0, toRead); 5905 if (nRead >= 0) mBytes += nRead; 5906 if (nRead <= 0) break; 5907 toCopy -= nRead; 5908 5909 // send it to the output pipe as long as things 5910 // are still good 5911 if (pipeOkay) { 5912 try { 5913 pipe.write(buffer, 0, nRead); 5914 } catch (IOException e) { 5915 Slog.e(TAG, "Failed to write to restore pipe", e); 5916 pipeOkay = false; 5917 } 5918 } 5919 } 5920 5921 // done sending that file! Now we just need to consume 5922 // the delta from info.size to the end of block. 5923 skipTarPadding(info.size, instream); 5924 5925 // and now that we've sent it all, wait for the remote 5926 // side to acknowledge receipt 5927 agentSuccess = waitUntilOperationComplete(token); 5928 } 5929 5930 // okay, if the remote end failed at any point, deal with 5931 // it by ignoring the rest of the restore on it 5932 if (!agentSuccess) { 5933 mBackupHandler.removeMessages(MSG_TIMEOUT); 5934 tearDownPipes(); 5935 tearDownAgent(mTargetApp); 5936 mAgent = null; 5937 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5938 } 5939 } 5940 5941 // Problems setting up the agent communication, or an already- 5942 // ignored package: skip to the next tar stream entry by 5943 // reading and discarding this file. 5944 if (!okay) { 5945 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 5946 long bytesToConsume = (info.size + 511) & ~511; 5947 while (bytesToConsume > 0) { 5948 int toRead = (bytesToConsume > buffer.length) 5949 ? buffer.length : (int)bytesToConsume; 5950 long nRead = instream.read(buffer, 0, toRead); 5951 if (nRead >= 0) mBytes += nRead; 5952 if (nRead <= 0) break; 5953 bytesToConsume -= nRead; 5954 } 5955 } 5956 } 5957 } 5958 } catch (IOException e) { 5959 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 5960 // treat as EOF 5961 info = null; 5962 } 5963 5964 return (info != null); 5965 } 5966 5967 void setUpPipes() throws IOException { 5968 mPipes = ParcelFileDescriptor.createPipe(); 5969 } 5970 5971 void tearDownPipes() { 5972 if (mPipes != null) { 5973 try { 5974 mPipes[0].close(); 5975 mPipes[0] = null; 5976 mPipes[1].close(); 5977 mPipes[1] = null; 5978 } catch (IOException e) { 5979 Slog.w(TAG, "Couldn't close agent pipes", e); 5980 } 5981 mPipes = null; 5982 } 5983 } 5984 5985 void tearDownAgent(ApplicationInfo app) { 5986 if (mAgent != null) { 5987 try { 5988 // unbind and tidy up even on timeout or failure, just in case 5989 mActivityManager.unbindBackupAgent(app); 5990 5991 // The agent was running with a stub Application object, so shut it down. 5992 // !!! We hardcode the confirmation UI's package name here rather than use a 5993 // manifest flag! TODO something less direct. 5994 if (app.uid != Process.SYSTEM_UID 5995 && !app.packageName.equals("com.android.backupconfirm")) { 5996 if (DEBUG) Slog.d(TAG, "Killing host process"); 5997 mActivityManager.killApplicationProcess(app.processName, app.uid); 5998 } else { 5999 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 6000 } 6001 } catch (RemoteException e) { 6002 Slog.d(TAG, "Lost app trying to shut down"); 6003 } 6004 mAgent = null; 6005 } 6006 } 6007 6008 class RestoreInstallObserver extends IPackageInstallObserver.Stub { 6009 final AtomicBoolean mDone = new AtomicBoolean(); 6010 String mPackageName; 6011 int mResult; 6012 6013 public void reset() { 6014 synchronized (mDone) { 6015 mDone.set(false); 6016 } 6017 } 6018 6019 public void waitForCompletion() { 6020 synchronized (mDone) { 6021 while (mDone.get() == false) { 6022 try { 6023 mDone.wait(); 6024 } catch (InterruptedException e) { } 6025 } 6026 } 6027 } 6028 6029 int getResult() { 6030 return mResult; 6031 } 6032 6033 @Override 6034 public void packageInstalled(String packageName, int returnCode) 6035 throws RemoteException { 6036 synchronized (mDone) { 6037 mResult = returnCode; 6038 mPackageName = packageName; 6039 mDone.set(true); 6040 mDone.notifyAll(); 6041 } 6042 } 6043 } 6044 6045 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 6046 final AtomicBoolean mDone = new AtomicBoolean(); 6047 int mResult; 6048 6049 public void reset() { 6050 synchronized (mDone) { 6051 mDone.set(false); 6052 } 6053 } 6054 6055 public void waitForCompletion() { 6056 synchronized (mDone) { 6057 while (mDone.get() == false) { 6058 try { 6059 mDone.wait(); 6060 } catch (InterruptedException e) { } 6061 } 6062 } 6063 } 6064 6065 @Override 6066 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 6067 synchronized (mDone) { 6068 mResult = returnCode; 6069 mDone.set(true); 6070 mDone.notifyAll(); 6071 } 6072 } 6073 } 6074 6075 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 6076 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 6077 6078 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 6079 boolean okay = true; 6080 6081 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 6082 6083 // The file content is an .apk file. Copy it out to a staging location and 6084 // attempt to install it. 6085 File apkFile = new File(mDataDir, info.packageName); 6086 try { 6087 FileOutputStream apkStream = new FileOutputStream(apkFile); 6088 byte[] buffer = new byte[32 * 1024]; 6089 long size = info.size; 6090 while (size > 0) { 6091 long toRead = (buffer.length < size) ? buffer.length : size; 6092 int didRead = instream.read(buffer, 0, (int)toRead); 6093 if (didRead >= 0) mBytes += didRead; 6094 apkStream.write(buffer, 0, didRead); 6095 size -= didRead; 6096 } 6097 apkStream.close(); 6098 6099 // make sure the installer can read it 6100 apkFile.setReadable(true, false); 6101 6102 // Now install it 6103 Uri packageUri = Uri.fromFile(apkFile); 6104 mInstallObserver.reset(); 6105 mPackageManager.installPackage(packageUri, mInstallObserver, 6106 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 6107 installerPackage); 6108 mInstallObserver.waitForCompletion(); 6109 6110 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 6111 // The only time we continue to accept install of data even if the 6112 // apk install failed is if we had already determined that we could 6113 // accept the data regardless. 6114 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 6115 okay = false; 6116 } 6117 } else { 6118 // Okay, the install succeeded. Make sure it was the right app. 6119 boolean uninstall = false; 6120 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 6121 Slog.w(TAG, "Restore stream claimed to include apk for " 6122 + info.packageName + " but apk was really " 6123 + mInstallObserver.mPackageName); 6124 // delete the package we just put in place; it might be fraudulent 6125 okay = false; 6126 uninstall = true; 6127 } else { 6128 try { 6129 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 6130 PackageManager.GET_SIGNATURES); 6131 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 6132 Slog.w(TAG, "Restore stream contains apk of package " 6133 + info.packageName + " but it disallows backup/restore"); 6134 okay = false; 6135 } else { 6136 // So far so good -- do the signatures match the manifest? 6137 Signature[] sigs = mManifestSignatures.get(info.packageName); 6138 if (signaturesMatch(sigs, pkg)) { 6139 // If this is a system-uid app without a declared backup agent, 6140 // don't restore any of the file data. 6141 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 6142 && (pkg.applicationInfo.backupAgentName == null)) { 6143 Slog.w(TAG, "Installed app " + info.packageName 6144 + " has restricted uid and no agent"); 6145 okay = false; 6146 } 6147 } else { 6148 Slog.w(TAG, "Installed app " + info.packageName 6149 + " signatures do not match restore manifest"); 6150 okay = false; 6151 uninstall = true; 6152 } 6153 } 6154 } catch (NameNotFoundException e) { 6155 Slog.w(TAG, "Install of package " + info.packageName 6156 + " succeeded but now not found"); 6157 okay = false; 6158 } 6159 } 6160 6161 // If we're not okay at this point, we need to delete the package 6162 // that we just installed. 6163 if (uninstall) { 6164 mDeleteObserver.reset(); 6165 mPackageManager.deletePackage(mInstallObserver.mPackageName, 6166 mDeleteObserver, 0); 6167 mDeleteObserver.waitForCompletion(); 6168 } 6169 } 6170 } catch (IOException e) { 6171 Slog.e(TAG, "Unable to transcribe restored apk for install"); 6172 okay = false; 6173 } finally { 6174 apkFile.delete(); 6175 } 6176 6177 return okay; 6178 } 6179 6180 // Given an actual file content size, consume the post-content padding mandated 6181 // by the tar format. 6182 void skipTarPadding(long size, InputStream instream) throws IOException { 6183 long partial = (size + 512) % 512; 6184 if (partial > 0) { 6185 final int needed = 512 - (int)partial; 6186 byte[] buffer = new byte[needed]; 6187 if (readExactly(instream, buffer, 0, needed) == needed) { 6188 mBytes += needed; 6189 } else throw new IOException("Unexpected EOF in padding"); 6190 } 6191 } 6192 6193 // Read a widget metadata file, returning the restored blob 6194 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 6195 // Fail on suspiciously large widget dump files 6196 if (info.size > 64 * 1024) { 6197 throw new IOException("Metadata too big; corrupt? size=" + info.size); 6198 } 6199 6200 byte[] buffer = new byte[(int) info.size]; 6201 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 6202 mBytes += info.size; 6203 } else throw new IOException("Unexpected EOF in widget data"); 6204 6205 String[] str = new String[1]; 6206 int offset = extractLine(buffer, 0, str); 6207 int version = Integer.parseInt(str[0]); 6208 if (version == BACKUP_MANIFEST_VERSION) { 6209 offset = extractLine(buffer, offset, str); 6210 final String pkg = str[0]; 6211 if (info.packageName.equals(pkg)) { 6212 // Data checks out -- the rest of the buffer is a concatenation of 6213 // binary blobs as described in the comment at writeAppWidgetData() 6214 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 6215 offset, buffer.length - offset); 6216 DataInputStream in = new DataInputStream(bin); 6217 while (bin.available() > 0) { 6218 int token = in.readInt(); 6219 int size = in.readInt(); 6220 if (size > 64 * 1024) { 6221 throw new IOException("Datum " 6222 + Integer.toHexString(token) 6223 + " too big; corrupt? size=" + info.size); 6224 } 6225 switch (token) { 6226 case BACKUP_WIDGET_METADATA_TOKEN: 6227 { 6228 if (MORE_DEBUG) { 6229 Slog.i(TAG, "Got widget metadata for " + info.packageName); 6230 } 6231 mWidgetData = new byte[size]; 6232 in.read(mWidgetData); 6233 break; 6234 } 6235 default: 6236 { 6237 if (DEBUG) { 6238 Slog.i(TAG, "Ignoring metadata blob " 6239 + Integer.toHexString(token) 6240 + " for " + info.packageName); 6241 } 6242 in.skipBytes(size); 6243 break; 6244 } 6245 } 6246 } 6247 } else { 6248 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 6249 + " but widget data for " + pkg); 6250 } 6251 } else { 6252 Slog.w(TAG, "Unsupported metadata version " + version); 6253 } 6254 } 6255 6256 // Returns a policy constant; takes a buffer arg to reduce memory churn 6257 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 6258 throws IOException { 6259 // Fail on suspiciously large manifest files 6260 if (info.size > 64 * 1024) { 6261 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 6262 } 6263 6264 byte[] buffer = new byte[(int) info.size]; 6265 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 6266 mBytes += info.size; 6267 } else throw new IOException("Unexpected EOF in manifest"); 6268 6269 RestorePolicy policy = RestorePolicy.IGNORE; 6270 String[] str = new String[1]; 6271 int offset = 0; 6272 6273 try { 6274 offset = extractLine(buffer, offset, str); 6275 int version = Integer.parseInt(str[0]); 6276 if (version == BACKUP_MANIFEST_VERSION) { 6277 offset = extractLine(buffer, offset, str); 6278 String manifestPackage = str[0]; 6279 // TODO: handle <original-package> 6280 if (manifestPackage.equals(info.packageName)) { 6281 offset = extractLine(buffer, offset, str); 6282 version = Integer.parseInt(str[0]); // app version 6283 offset = extractLine(buffer, offset, str); 6284 int platformVersion = Integer.parseInt(str[0]); 6285 offset = extractLine(buffer, offset, str); 6286 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 6287 offset = extractLine(buffer, offset, str); 6288 boolean hasApk = str[0].equals("1"); 6289 offset = extractLine(buffer, offset, str); 6290 int numSigs = Integer.parseInt(str[0]); 6291 if (numSigs > 0) { 6292 Signature[] sigs = new Signature[numSigs]; 6293 for (int i = 0; i < numSigs; i++) { 6294 offset = extractLine(buffer, offset, str); 6295 sigs[i] = new Signature(str[0]); 6296 } 6297 mManifestSignatures.put(info.packageName, sigs); 6298 6299 // Okay, got the manifest info we need... 6300 try { 6301 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 6302 info.packageName, PackageManager.GET_SIGNATURES); 6303 // Fall through to IGNORE if the app explicitly disallows backup 6304 final int flags = pkgInfo.applicationInfo.flags; 6305 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 6306 // Restore system-uid-space packages only if they have 6307 // defined a custom backup agent 6308 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 6309 || (pkgInfo.applicationInfo.backupAgentName != null)) { 6310 // Verify signatures against any installed version; if they 6311 // don't match, then we fall though and ignore the data. The 6312 // signatureMatch() method explicitly ignores the signature 6313 // check for packages installed on the system partition, because 6314 // such packages are signed with the platform cert instead of 6315 // the app developer's cert, so they're different on every 6316 // device. 6317 if (signaturesMatch(sigs, pkgInfo)) { 6318 if (pkgInfo.versionCode >= version) { 6319 Slog.i(TAG, "Sig + version match; taking data"); 6320 policy = RestorePolicy.ACCEPT; 6321 } else { 6322 // The data is from a newer version of the app than 6323 // is presently installed. That means we can only 6324 // use it if the matching apk is also supplied. 6325 Slog.d(TAG, "Data version " + version 6326 + " is newer than installed version " 6327 + pkgInfo.versionCode + " - requiring apk"); 6328 policy = RestorePolicy.ACCEPT_IF_APK; 6329 } 6330 } else { 6331 Slog.w(TAG, "Restore manifest signatures do not match " 6332 + "installed application for " + info.packageName); 6333 } 6334 } else { 6335 Slog.w(TAG, "Package " + info.packageName 6336 + " is system level with no agent"); 6337 } 6338 } else { 6339 if (DEBUG) Slog.i(TAG, "Restore manifest from " 6340 + info.packageName + " but allowBackup=false"); 6341 } 6342 } catch (NameNotFoundException e) { 6343 // Okay, the target app isn't installed. We can process 6344 // the restore properly only if the dataset provides the 6345 // apk file and we can successfully install it. 6346 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 6347 + " not installed; requiring apk in dataset"); 6348 policy = RestorePolicy.ACCEPT_IF_APK; 6349 } 6350 6351 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 6352 Slog.i(TAG, "Cannot restore package " + info.packageName 6353 + " without the matching .apk"); 6354 } 6355 } else { 6356 Slog.i(TAG, "Missing signature on backed-up package " 6357 + info.packageName); 6358 } 6359 } else { 6360 Slog.i(TAG, "Expected package " + info.packageName 6361 + " but restore manifest claims " + manifestPackage); 6362 } 6363 } else { 6364 Slog.i(TAG, "Unknown restore manifest version " + version 6365 + " for package " + info.packageName); 6366 } 6367 } catch (NumberFormatException e) { 6368 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 6369 } catch (IllegalArgumentException e) { 6370 Slog.w(TAG, e.getMessage()); 6371 } 6372 6373 return policy; 6374 } 6375 6376 // Builds a line from a byte buffer starting at 'offset', and returns 6377 // the index of the next unconsumed data in the buffer. 6378 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 6379 final int end = buffer.length; 6380 if (offset >= end) throw new IOException("Incomplete data"); 6381 6382 int pos; 6383 for (pos = offset; pos < end; pos++) { 6384 byte c = buffer[pos]; 6385 // at LF we declare end of line, and return the next char as the 6386 // starting point for the next time through 6387 if (c == '\n') { 6388 break; 6389 } 6390 } 6391 outStr[0] = new String(buffer, offset, pos - offset); 6392 pos++; // may be pointing an extra byte past the end but that's okay 6393 return pos; 6394 } 6395 6396 void dumpFileMetadata(FileMetadata info) { 6397 if (DEBUG) { 6398 StringBuilder b = new StringBuilder(128); 6399 6400 // mode string 6401 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 6402 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 6403 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 6404 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 6405 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 6406 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 6407 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 6408 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 6409 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 6410 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 6411 b.append(String.format(" %9d ", info.size)); 6412 6413 Date stamp = new Date(info.mtime); 6414 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 6415 6416 b.append(info.packageName); 6417 b.append(" :: "); 6418 b.append(info.domain); 6419 b.append(" :: "); 6420 b.append(info.path); 6421 6422 Slog.i(TAG, b.toString()); 6423 } 6424 } 6425 // Consume a tar file header block [sequence] and accumulate the relevant metadata 6426 FileMetadata readTarHeaders(InputStream instream) throws IOException { 6427 byte[] block = new byte[512]; 6428 FileMetadata info = null; 6429 6430 boolean gotHeader = readTarHeader(instream, block); 6431 if (gotHeader) { 6432 try { 6433 // okay, presume we're okay, and extract the various metadata 6434 info = new FileMetadata(); 6435 info.size = extractRadix(block, 124, 12, 8); 6436 info.mtime = extractRadix(block, 136, 12, 8); 6437 info.mode = extractRadix(block, 100, 8, 8); 6438 6439 info.path = extractString(block, 345, 155); // prefix 6440 String path = extractString(block, 0, 100); 6441 if (path.length() > 0) { 6442 if (info.path.length() > 0) info.path += '/'; 6443 info.path += path; 6444 } 6445 6446 // tar link indicator field: 1 byte at offset 156 in the header. 6447 int typeChar = block[156]; 6448 if (typeChar == 'x') { 6449 // pax extended header, so we need to read that 6450 gotHeader = readPaxExtendedHeader(instream, info); 6451 if (gotHeader) { 6452 // and after a pax extended header comes another real header -- read 6453 // that to find the real file type 6454 gotHeader = readTarHeader(instream, block); 6455 } 6456 if (!gotHeader) throw new IOException("Bad or missing pax header"); 6457 6458 typeChar = block[156]; 6459 } 6460 6461 switch (typeChar) { 6462 case '0': info.type = BackupAgent.TYPE_FILE; break; 6463 case '5': { 6464 info.type = BackupAgent.TYPE_DIRECTORY; 6465 if (info.size != 0) { 6466 Slog.w(TAG, "Directory entry with nonzero size in header"); 6467 info.size = 0; 6468 } 6469 break; 6470 } 6471 case 0: { 6472 // presume EOF 6473 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 6474 return null; 6475 } 6476 default: { 6477 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 6478 throw new IOException("Unknown entity type " + typeChar); 6479 } 6480 } 6481 6482 // Parse out the path 6483 // 6484 // first: apps/shared/unrecognized 6485 if (FullBackup.SHARED_PREFIX.regionMatches(0, 6486 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 6487 // File in shared storage. !!! TODO: implement this. 6488 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 6489 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 6490 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 6491 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 6492 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 6493 info.path, 0, FullBackup.APPS_PREFIX.length())) { 6494 // App content! Parse out the package name and domain 6495 6496 // strip the apps/ prefix 6497 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 6498 6499 // extract the package name 6500 int slash = info.path.indexOf('/'); 6501 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 6502 info.packageName = info.path.substring(0, slash); 6503 info.path = info.path.substring(slash+1); 6504 6505 // if it's a manifest or metadata payload we're done, otherwise parse 6506 // out the domain into which the file will be restored 6507 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 6508 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 6509 slash = info.path.indexOf('/'); 6510 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path); 6511 info.domain = info.path.substring(0, slash); 6512 info.path = info.path.substring(slash + 1); 6513 } 6514 } 6515 } catch (IOException e) { 6516 if (DEBUG) { 6517 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 6518 HEXLOG(block); 6519 } 6520 throw e; 6521 } 6522 } 6523 return info; 6524 } 6525 6526 private void HEXLOG(byte[] block) { 6527 int offset = 0; 6528 int todo = block.length; 6529 StringBuilder buf = new StringBuilder(64); 6530 while (todo > 0) { 6531 buf.append(String.format("%04x ", offset)); 6532 int numThisLine = (todo > 16) ? 16 : todo; 6533 for (int i = 0; i < numThisLine; i++) { 6534 buf.append(String.format("%02x ", block[offset+i])); 6535 } 6536 Slog.i("hexdump", buf.toString()); 6537 buf.setLength(0); 6538 todo -= numThisLine; 6539 offset += numThisLine; 6540 } 6541 } 6542 6543 // Read exactly the given number of bytes into a buffer at the stated offset. 6544 // Returns false if EOF is encountered before the requested number of bytes 6545 // could be read. 6546 int readExactly(InputStream in, byte[] buffer, int offset, int size) 6547 throws IOException { 6548 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 6549 6550 int soFar = 0; 6551 while (soFar < size) { 6552 int nRead = in.read(buffer, offset + soFar, size - soFar); 6553 if (nRead <= 0) { 6554 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 6555 break; 6556 } 6557 soFar += nRead; 6558 } 6559 return soFar; 6560 } 6561 6562 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 6563 final int got = readExactly(instream, block, 0, 512); 6564 if (got == 0) return false; // Clean EOF 6565 if (got < 512) throw new IOException("Unable to read full block header"); 6566 mBytes += 512; 6567 return true; 6568 } 6569 6570 // overwrites 'info' fields based on the pax extended header 6571 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 6572 throws IOException { 6573 // We should never see a pax extended header larger than this 6574 if (info.size > 32*1024) { 6575 Slog.w(TAG, "Suspiciously large pax header size " + info.size 6576 + " - aborting"); 6577 throw new IOException("Sanity failure: pax header size " + info.size); 6578 } 6579 6580 // read whole blocks, not just the content size 6581 int numBlocks = (int)((info.size + 511) >> 9); 6582 byte[] data = new byte[numBlocks * 512]; 6583 if (readExactly(instream, data, 0, data.length) < data.length) { 6584 throw new IOException("Unable to read full pax header"); 6585 } 6586 mBytes += data.length; 6587 6588 final int contentSize = (int) info.size; 6589 int offset = 0; 6590 do { 6591 // extract the line at 'offset' 6592 int eol = offset+1; 6593 while (eol < contentSize && data[eol] != ' ') eol++; 6594 if (eol >= contentSize) { 6595 // error: we just hit EOD looking for the end of the size field 6596 throw new IOException("Invalid pax data"); 6597 } 6598 // eol points to the space between the count and the key 6599 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 6600 int key = eol + 1; // start of key=value 6601 eol = offset + linelen - 1; // trailing LF 6602 int value; 6603 for (value = key+1; data[value] != '=' && value <= eol; value++); 6604 if (value > eol) { 6605 throw new IOException("Invalid pax declaration"); 6606 } 6607 6608 // pax requires that key/value strings be in UTF-8 6609 String keyStr = new String(data, key, value-key, "UTF-8"); 6610 // -1 to strip the trailing LF 6611 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 6612 6613 if ("path".equals(keyStr)) { 6614 info.path = valStr; 6615 } else if ("size".equals(keyStr)) { 6616 info.size = Long.parseLong(valStr); 6617 } else { 6618 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 6619 } 6620 6621 offset += linelen; 6622 } while (offset < contentSize); 6623 6624 return true; 6625 } 6626 6627 long extractRadix(byte[] data, int offset, int maxChars, int radix) 6628 throws IOException { 6629 long value = 0; 6630 final int end = offset + maxChars; 6631 for (int i = offset; i < end; i++) { 6632 final byte b = data[i]; 6633 // Numeric fields in tar can terminate with either NUL or SPC 6634 if (b == 0 || b == ' ') break; 6635 if (b < '0' || b > ('0' + radix - 1)) { 6636 throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix); 6637 } 6638 value = radix * value + (b - '0'); 6639 } 6640 return value; 6641 } 6642 6643 String extractString(byte[] data, int offset, int maxChars) throws IOException { 6644 final int end = offset + maxChars; 6645 int eos = offset; 6646 // tar string fields terminate early with a NUL 6647 while (eos < end && data[eos] != 0) eos++; 6648 return new String(data, offset, eos-offset, "US-ASCII"); 6649 } 6650 6651 void sendStartRestore() { 6652 if (mObserver != null) { 6653 try { 6654 mObserver.onStartRestore(); 6655 } catch (RemoteException e) { 6656 Slog.w(TAG, "full restore observer went away: startRestore"); 6657 mObserver = null; 6658 } 6659 } 6660 } 6661 6662 void sendOnRestorePackage(String name) { 6663 if (mObserver != null) { 6664 try { 6665 // TODO: use a more user-friendly name string 6666 mObserver.onRestorePackage(name); 6667 } catch (RemoteException e) { 6668 Slog.w(TAG, "full restore observer went away: restorePackage"); 6669 mObserver = null; 6670 } 6671 } 6672 } 6673 6674 void sendEndRestore() { 6675 if (mObserver != null) { 6676 try { 6677 mObserver.onEndRestore(); 6678 } catch (RemoteException e) { 6679 Slog.w(TAG, "full restore observer went away: endRestore"); 6680 mObserver = null; 6681 } 6682 } 6683 } 6684 } 6685 6686 // ----- Restore handling ----- 6687 6688 // new style: we only store the SHA-1 hashes of each sig, not the full block 6689 static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) { 6690 if (target == null) { 6691 return false; 6692 } 6693 6694 // If the target resides on the system partition, we allow it to restore 6695 // data from the like-named package in a restore set even if the signatures 6696 // do not match. (Unlike general applications, those flashed to the system 6697 // partition will be signed with the device's platform certificate, so on 6698 // different phones the same system app will have different signatures.) 6699 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 6700 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 6701 return true; 6702 } 6703 6704 // Allow unsigned apps, but not signed on one device and unsigned on the other 6705 // !!! TODO: is this the right policy? 6706 Signature[] deviceSigs = target.signatures; 6707 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes 6708 + " device=" + deviceSigs); 6709 if ((storedSigHashes == null || storedSigHashes.size() == 0) 6710 && (deviceSigs == null || deviceSigs.length == 0)) { 6711 return true; 6712 } 6713 if (storedSigHashes == null || deviceSigs == null) { 6714 return false; 6715 } 6716 6717 // !!! TODO: this demands that every stored signature match one 6718 // that is present on device, and does not demand the converse. 6719 // Is this this right policy? 6720 final int nStored = storedSigHashes.size(); 6721 final int nDevice = deviceSigs.length; 6722 6723 // hash each on-device signature 6724 ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice); 6725 for (int i = 0; i < nDevice; i++) { 6726 deviceHashes.add(hashSignature(deviceSigs[i])); 6727 } 6728 6729 // now ensure that each stored sig (hash) matches an on-device sig (hash) 6730 for (int n = 0; n < nStored; n++) { 6731 boolean match = false; 6732 final byte[] storedHash = storedSigHashes.get(n); 6733 for (int i = 0; i < nDevice; i++) { 6734 if (Arrays.equals(storedHash, deviceHashes.get(i))) { 6735 match = true; 6736 break; 6737 } 6738 } 6739 // match is false when no on-device sig matched one of the stored ones 6740 if (!match) { 6741 return false; 6742 } 6743 } 6744 6745 return true; 6746 } 6747 6748 static byte[] hashSignature(Signature sig) { 6749 try { 6750 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 6751 digest.update(sig.toByteArray()); 6752 return digest.digest(); 6753 } catch (NoSuchAlgorithmException e) { 6754 Slog.w(TAG, "No SHA-256 algorithm found!"); 6755 } 6756 return null; 6757 } 6758 6759 // Old style: directly match the stored vs on device signature blocks 6760 static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 6761 if (target == null) { 6762 return false; 6763 } 6764 6765 // If the target resides on the system partition, we allow it to restore 6766 // data from the like-named package in a restore set even if the signatures 6767 // do not match. (Unlike general applications, those flashed to the system 6768 // partition will be signed with the device's platform certificate, so on 6769 // different phones the same system app will have different signatures.) 6770 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 6771 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 6772 return true; 6773 } 6774 6775 // Allow unsigned apps, but not signed on one device and unsigned on the other 6776 // !!! TODO: is this the right policy? 6777 Signature[] deviceSigs = target.signatures; 6778 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs 6779 + " device=" + deviceSigs); 6780 if ((storedSigs == null || storedSigs.length == 0) 6781 && (deviceSigs == null || deviceSigs.length == 0)) { 6782 return true; 6783 } 6784 if (storedSigs == null || deviceSigs == null) { 6785 return false; 6786 } 6787 6788 // !!! TODO: this demands that every stored signature match one 6789 // that is present on device, and does not demand the converse. 6790 // Is this this right policy? 6791 int nStored = storedSigs.length; 6792 int nDevice = deviceSigs.length; 6793 6794 for (int i=0; i < nStored; i++) { 6795 boolean match = false; 6796 for (int j=0; j < nDevice; j++) { 6797 if (storedSigs[i].equals(deviceSigs[j])) { 6798 match = true; 6799 break; 6800 } 6801 } 6802 if (!match) { 6803 return false; 6804 } 6805 } 6806 return true; 6807 } 6808 6809 // Used by both incremental and full restore 6810 void restoreWidgetData(String packageName, byte[] widgetData) { 6811 // Apply the restored widget state and generate the ID update for the app 6812 AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER); 6813 } 6814 6815 // ***************************** 6816 // NEW UNIFIED RESTORE IMPLEMENTATION 6817 // ***************************** 6818 6819 // states of the unified-restore state machine 6820 enum UnifiedRestoreState { 6821 INITIAL, 6822 RUNNING_QUEUE, 6823 RESTORE_KEYVALUE, 6824 RESTORE_FULL, 6825 RESTORE_FINISHED, 6826 FINAL 6827 } 6828 6829 class PerformUnifiedRestoreTask implements BackupRestoreTask { 6830 // Transport we're working with to do the restore 6831 private IBackupTransport mTransport; 6832 6833 // Where per-transport saved state goes 6834 File mStateDir; 6835 6836 // Restore observer; may be null 6837 private IRestoreObserver mObserver; 6838 6839 // Token identifying the dataset to the transport 6840 private long mToken; 6841 6842 // When this is a restore-during-install, this is the token identifying the 6843 // operation to the Package Manager, and we must ensure that we let it know 6844 // when we're finished. 6845 private int mPmToken; 6846 6847 // Is this a whole-system restore, i.e. are we establishing a new ancestral 6848 // dataset to base future restore-at-install operations from? 6849 private boolean mIsSystemRestore; 6850 6851 // If this is a single-package restore, what package are we interested in? 6852 private PackageInfo mTargetPackage; 6853 6854 // In all cases, the calculated list of packages that we are trying to restore 6855 private List<PackageInfo> mAcceptSet; 6856 6857 // Our bookkeeping about the ancestral dataset 6858 private PackageManagerBackupAgent mPmAgent; 6859 6860 // Currently-bound backup agent for restore + restoreFinished purposes 6861 private IBackupAgent mAgent; 6862 6863 // What sort of restore we're doing now 6864 private RestoreDescription mRestoreDescription; 6865 6866 // The package we're currently restoring 6867 private PackageInfo mCurrentPackage; 6868 6869 // Widget-related data handled as part of this restore operation 6870 private byte[] mWidgetData; 6871 6872 // Number of apps restored in this pass 6873 private int mCount; 6874 6875 // When did we start? 6876 private long mStartRealtime; 6877 6878 // State machine progress 6879 private UnifiedRestoreState mState; 6880 6881 // How are things going? 6882 private int mStatus; 6883 6884 // Done? 6885 private boolean mFinished; 6886 6887 // Key/value: bookkeeping about staged data and files for agent access 6888 private File mBackupDataName; 6889 private File mStageName; 6890 private File mSavedStateName; 6891 private File mNewStateName; 6892 ParcelFileDescriptor mBackupData; 6893 ParcelFileDescriptor mNewState; 6894 6895 // Invariant: mWakelock is already held, and this task is responsible for 6896 // releasing it at the end of the restore operation. 6897 PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer, 6898 long restoreSetToken, PackageInfo targetPackage, int pmToken, 6899 boolean isFullSystemRestore, String[] filterSet) { 6900 mState = UnifiedRestoreState.INITIAL; 6901 mStartRealtime = SystemClock.elapsedRealtime(); 6902 6903 mTransport = transport; 6904 mObserver = observer; 6905 mToken = restoreSetToken; 6906 mPmToken = pmToken; 6907 mTargetPackage = targetPackage; 6908 mIsSystemRestore = isFullSystemRestore; 6909 mFinished = false; 6910 6911 if (targetPackage != null) { 6912 // Single package restore 6913 mAcceptSet = new ArrayList<PackageInfo>(); 6914 mAcceptSet.add(targetPackage); 6915 } else { 6916 // Everything possible, or a target set 6917 if (filterSet == null) { 6918 // We want everything and a pony 6919 List<PackageInfo> apps = 6920 PackageManagerBackupAgent.getStorableApplications(mPackageManager); 6921 filterSet = packagesToNames(apps); 6922 if (DEBUG) { 6923 Slog.i(TAG, "Full restore; asking for " + filterSet.length + " apps"); 6924 } 6925 } 6926 6927 mAcceptSet = new ArrayList<PackageInfo>(filterSet.length); 6928 6929 // Pro tem, we insist on moving the settings provider package to last place. 6930 // Keep track of whether it's in the list, and bump it down if so. We also 6931 // want to do the system package itself first if it's called for. 6932 boolean hasSystem = false; 6933 boolean hasSettings = false; 6934 for (int i = 0; i < filterSet.length; i++) { 6935 try { 6936 PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0); 6937 if ("android".equals(info.packageName)) { 6938 hasSystem = true; 6939 continue; 6940 } 6941 if (SETTINGS_PACKAGE.equals(info.packageName)) { 6942 hasSettings = true; 6943 continue; 6944 } 6945 6946 if (appIsEligibleForBackup(info.applicationInfo)) { 6947 mAcceptSet.add(info); 6948 } 6949 } catch (NameNotFoundException e) { 6950 // requested package name doesn't exist; ignore it 6951 } 6952 } 6953 if (hasSystem) { 6954 try { 6955 mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0)); 6956 } catch (NameNotFoundException e) { 6957 // won't happen; we know a priori that it's valid 6958 } 6959 } 6960 if (hasSettings) { 6961 try { 6962 mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0)); 6963 } catch (NameNotFoundException e) { 6964 // this one is always valid too 6965 } 6966 } 6967 } 6968 6969 if (MORE_DEBUG) { 6970 Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size()); 6971 for (PackageInfo info : mAcceptSet) { 6972 Slog.v(TAG, " " + info.packageName); 6973 } 6974 } 6975 } 6976 6977 private String[] packagesToNames(List<PackageInfo> apps) { 6978 final int N = apps.size(); 6979 String[] names = new String[N]; 6980 for (int i = 0; i < N; i++) { 6981 names[i] = apps.get(i).packageName; 6982 } 6983 return names; 6984 } 6985 6986 // Execute one tick of whatever state machine the task implements 6987 @Override 6988 public void execute() { 6989 if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState); 6990 switch (mState) { 6991 case INITIAL: 6992 startRestore(); 6993 break; 6994 6995 case RUNNING_QUEUE: 6996 dispatchNextRestore(); 6997 break; 6998 6999 case RESTORE_KEYVALUE: 7000 restoreKeyValue(); 7001 break; 7002 7003 case RESTORE_FULL: 7004 restoreFull(); 7005 break; 7006 7007 case RESTORE_FINISHED: 7008 restoreFinished(); 7009 break; 7010 7011 case FINAL: 7012 if (!mFinished) finalizeRestore(); 7013 else { 7014 Slog.e(TAG, "Duplicate finish"); 7015 } 7016 mFinished = true; 7017 break; 7018 } 7019 } 7020 7021 /* 7022 * SKETCH OF OPERATION 7023 * 7024 * create one of these PerformUnifiedRestoreTask objects, telling it which 7025 * dataset & transport to address, and then parameters within the restore 7026 * operation: single target package vs many, etc. 7027 * 7028 * 1. transport.startRestore(token, list-of-packages). If we need @pm@ it is 7029 * always placed first and the settings provider always placed last [for now]. 7030 * 7031 * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline] 7032 * 7033 * [ state change => RUNNING_QUEUE ] 7034 * 7035 * NOW ITERATE: 7036 * 7037 * { 3. t.nextRestorePackage() 7038 * 4. does the metadata for this package allow us to restore it? 7039 * does the on-disk app permit us to restore it? [re-check allowBackup etc] 7040 * 5. is this a key/value dataset? => key/value agent restore 7041 * [ state change => RESTORE_KEYVALUE ] 7042 * 5a. spin up agent 7043 * 5b. t.getRestoreData() to stage it properly 7044 * 5c. call into agent to perform restore 7045 * 5d. tear down agent 7046 * [ state change => RUNNING_QUEUE ] 7047 * 7048 * 6. else it's a stream dataset: 7049 * [ state change => RESTORE_FULL ] 7050 * 6a. instantiate the engine for a stream restore: engine handles agent lifecycles 7051 * 6b. spin off engine runner on separate thread 7052 * 6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket 7053 * [ state change => RUNNING_QUEUE ] 7054 * } 7055 * 7056 * [ state change => FINAL ] 7057 * 7058 * 7. t.finishRestore(), release wakelock, etc. 7059 * 7060 * 7061 */ 7062 7063 // state INITIAL : set up for the restore and read the metadata if necessary 7064 private void startRestore() { 7065 sendStartRestore(mAcceptSet.size()); 7066 7067 // If we're starting a full-system restore, set up to begin widget ID remapping 7068 if (mIsSystemRestore) { 7069 AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER); 7070 } 7071 7072 try { 7073 String transportDir = mTransport.transportDirName(); 7074 mStateDir = new File(mBaseStateDir, transportDir); 7075 7076 // Fetch the current metadata from the dataset first 7077 PackageInfo pmPackage = new PackageInfo(); 7078 pmPackage.packageName = PACKAGE_MANAGER_SENTINEL; 7079 mAcceptSet.add(0, pmPackage); 7080 7081 PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]); 7082 mStatus = mTransport.startRestore(mToken, packages); 7083 if (mStatus != BackupTransport.TRANSPORT_OK) { 7084 Slog.e(TAG, "Transport error " + mStatus + "; no restore possible"); 7085 mStatus = BackupTransport.TRANSPORT_ERROR; 7086 executeNextState(UnifiedRestoreState.FINAL); 7087 return; 7088 } 7089 7090 RestoreDescription desc = mTransport.nextRestorePackage(); 7091 if (desc == null) { 7092 Slog.e(TAG, "No restore metadata available; halting"); 7093 mStatus = BackupTransport.TRANSPORT_ERROR; 7094 executeNextState(UnifiedRestoreState.FINAL); 7095 return; 7096 } 7097 if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) { 7098 Slog.e(TAG, "Required metadata but got " + desc.getPackageName()); 7099 mStatus = BackupTransport.TRANSPORT_ERROR; 7100 executeNextState(UnifiedRestoreState.FINAL); 7101 return; 7102 } 7103 7104 // Pull the Package Manager metadata from the restore set first 7105 mCurrentPackage = new PackageInfo(); 7106 mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL; 7107 mPmAgent = new PackageManagerBackupAgent(mPackageManager, null); 7108 mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind()); 7109 if (MORE_DEBUG) { 7110 Slog.v(TAG, "initiating restore for PMBA"); 7111 } 7112 initiateOneRestore(mCurrentPackage, 0); 7113 // The PM agent called operationComplete() already, because our invocation 7114 // of it is process-local and therefore synchronous. That means that the 7115 // next-state message (RUNNING_QUEUE) is already enqueued. Only if we're 7116 // unable to proceed with running the queue do we remove that pending 7117 // message and jump straight to the FINAL state. 7118 7119 // Verify that the backup set includes metadata. If not, we can't do 7120 // signature/version verification etc, so we simply do not proceed with 7121 // the restore operation. 7122 if (!mPmAgent.hasMetadata()) { 7123 Slog.e(TAG, "No restore metadata available, so not restoring"); 7124 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7125 PACKAGE_MANAGER_SENTINEL, 7126 "Package manager restore metadata missing"); 7127 mStatus = BackupTransport.TRANSPORT_ERROR; 7128 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 7129 executeNextState(UnifiedRestoreState.FINAL); 7130 return; 7131 } 7132 7133 // Success; cache the metadata and continue as expected with the 7134 // next state already enqueued 7135 7136 } catch (RemoteException e) { 7137 // If we lost the transport at any time, halt 7138 Slog.e(TAG, "Unable to contact transport for restore"); 7139 mStatus = BackupTransport.TRANSPORT_ERROR; 7140 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 7141 executeNextState(UnifiedRestoreState.FINAL); 7142 return; 7143 } 7144 } 7145 7146 // state RUNNING_QUEUE : figure out what the next thing to be restored is, 7147 // and fire the appropriate next step 7148 private void dispatchNextRestore() { 7149 UnifiedRestoreState nextState = UnifiedRestoreState.FINAL; 7150 try { 7151 mRestoreDescription = mTransport.nextRestorePackage(); 7152 final String pkgName = (mRestoreDescription != null) 7153 ? mRestoreDescription.getPackageName() : null; 7154 if (pkgName == null) { 7155 Slog.e(TAG, "Failure getting next package name"); 7156 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7157 nextState = UnifiedRestoreState.FINAL; 7158 return; 7159 } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) { 7160 // Yay we've reached the end cleanly 7161 if (DEBUG) { 7162 Slog.v(TAG, "No more packages; finishing restore"); 7163 } 7164 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); 7165 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis); 7166 nextState = UnifiedRestoreState.FINAL; 7167 return; 7168 } 7169 7170 if (DEBUG) { 7171 Slog.i(TAG, "Next restore package: " + mRestoreDescription); 7172 } 7173 sendOnRestorePackage(pkgName); 7174 7175 Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName); 7176 if (metaInfo == null) { 7177 Slog.e(TAG, "No metadata for " + pkgName); 7178 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 7179 "Package metadata missing"); 7180 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7181 return; 7182 } 7183 7184 try { 7185 mCurrentPackage = mPackageManager.getPackageInfo( 7186 pkgName, PackageManager.GET_SIGNATURES); 7187 } catch (NameNotFoundException e) { 7188 // Whoops, we thought we could restore this package but it 7189 // turns out not to be present. Skip it. 7190 Slog.e(TAG, "Package not present: " + pkgName); 7191 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 7192 "Package missing on device"); 7193 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7194 return; 7195 } 7196 7197 if (metaInfo.versionCode > mCurrentPackage.versionCode) { 7198 // Data is from a "newer" version of the app than we have currently 7199 // installed. If the app has not declared that it is prepared to 7200 // handle this case, we do not attempt the restore. 7201 if ((mCurrentPackage.applicationInfo.flags 7202 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { 7203 String message = "Version " + metaInfo.versionCode 7204 + " > installed version " + mCurrentPackage.versionCode; 7205 Slog.w(TAG, "Package " + pkgName + ": " + message); 7206 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7207 pkgName, message); 7208 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7209 return; 7210 } else { 7211 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode 7212 + " > installed " + mCurrentPackage.versionCode 7213 + " but restoreAnyVersion"); 7214 } 7215 } 7216 7217 if (DEBUG) Slog.v(TAG, "Package " + pkgName 7218 + " restore version [" + metaInfo.versionCode 7219 + "] is compatible with installed version [" 7220 + mCurrentPackage.versionCode + "]"); 7221 7222 // Reset per-package preconditions and fire the appropriate next state 7223 mWidgetData = null; 7224 final int type = mRestoreDescription.getDataType(); 7225 if (type == RestoreDescription.TYPE_KEY_VALUE) { 7226 nextState = UnifiedRestoreState.RESTORE_KEYVALUE; 7227 } else if (type == RestoreDescription.TYPE_FULL_STREAM) { 7228 nextState = UnifiedRestoreState.RESTORE_FULL; 7229 } else { 7230 // Unknown restore type; ignore this package and move on 7231 Slog.e(TAG, "Unrecognized restore type " + type); 7232 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7233 return; 7234 } 7235 } catch (RemoteException e) { 7236 Slog.e(TAG, "Can't get next target from transport; ending restore"); 7237 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7238 nextState = UnifiedRestoreState.FINAL; 7239 return; 7240 } finally { 7241 executeNextState(nextState); 7242 } 7243 } 7244 7245 // state RESTORE_KEYVALUE : restore one package via key/value API set 7246 private void restoreKeyValue() { 7247 // Initiating the restore will pass responsibility for the state machine's 7248 // progress to the agent callback, so we do not always execute the 7249 // next state here. 7250 final String packageName = mCurrentPackage.packageName; 7251 // Validate some semantic requirements that apply in this way 7252 // only to the key/value restore API flow 7253 if (mCurrentPackage.applicationInfo.backupAgentName == null 7254 || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) { 7255 if (DEBUG) { 7256 Slog.i(TAG, "Data exists for package " + packageName 7257 + " but app has no agent; skipping"); 7258 } 7259 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 7260 "Package has no agent"); 7261 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7262 return; 7263 } 7264 7265 Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); 7266 if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { 7267 Slog.w(TAG, "Signature mismatch restoring " + packageName); 7268 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 7269 "Signature mismatch"); 7270 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7271 return; 7272 } 7273 7274 // Good to go! Set up and bind the agent... 7275 mAgent = bindToAgentSynchronous( 7276 mCurrentPackage.applicationInfo, 7277 IApplicationThread.BACKUP_MODE_INCREMENTAL); 7278 if (mAgent == null) { 7279 Slog.w(TAG, "Can't find backup agent for " + packageName); 7280 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 7281 "Restore agent missing"); 7282 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7283 return; 7284 } 7285 7286 // And then finally start the restore on this agent 7287 try { 7288 initiateOneRestore(mCurrentPackage, metaInfo.versionCode); 7289 ++mCount; 7290 } catch (Exception e) { 7291 Slog.e(TAG, "Error when attempting restore: " + e.toString()); 7292 keyValueAgentErrorCleanup(); 7293 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7294 } 7295 } 7296 7297 // Guts of a key/value restore operation 7298 void initiateOneRestore(PackageInfo app, int appVersionCode) { 7299 final String packageName = app.packageName; 7300 7301 if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName); 7302 7303 // !!! TODO: get the dirs from the transport 7304 mBackupDataName = new File(mDataDir, packageName + ".restore"); 7305 mStageName = new File(mDataDir, packageName + ".stage"); 7306 mNewStateName = new File(mStateDir, packageName + ".new"); 7307 mSavedStateName = new File(mStateDir, packageName); 7308 7309 // don't stage the 'android' package where the wallpaper data lives. this is 7310 // an optimization: we know there's no widget data hosted/published by that 7311 // package, and this way we avoid doing a spurious copy of MB-sized wallpaper 7312 // data following the download. 7313 boolean staging = !packageName.equals("android"); 7314 ParcelFileDescriptor stage; 7315 File downloadFile = (staging) ? mStageName : mBackupDataName; 7316 7317 final int token = generateToken(); 7318 try { 7319 // Run the transport's restore pass 7320 stage = ParcelFileDescriptor.open(downloadFile, 7321 ParcelFileDescriptor.MODE_READ_WRITE | 7322 ParcelFileDescriptor.MODE_CREATE | 7323 ParcelFileDescriptor.MODE_TRUNCATE); 7324 7325 if (!SELinux.restorecon(mBackupDataName)) { 7326 Slog.e(TAG, "SElinux restorecon failed for " + downloadFile); 7327 } 7328 7329 if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) { 7330 // Transport-level failure, so we wind everything up and 7331 // terminate the restore operation. 7332 Slog.e(TAG, "Error getting restore data for " + packageName); 7333 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7334 stage.close(); 7335 downloadFile.delete(); 7336 executeNextState(UnifiedRestoreState.FINAL); 7337 return; 7338 } 7339 7340 // We have the data from the transport. Now we extract and strip 7341 // any per-package metadata (typically widget-related information) 7342 // if appropriate 7343 if (staging) { 7344 stage.close(); 7345 stage = ParcelFileDescriptor.open(downloadFile, 7346 ParcelFileDescriptor.MODE_READ_ONLY); 7347 7348 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 7349 ParcelFileDescriptor.MODE_READ_WRITE | 7350 ParcelFileDescriptor.MODE_CREATE | 7351 ParcelFileDescriptor.MODE_TRUNCATE); 7352 7353 BackupDataInput in = new BackupDataInput(stage.getFileDescriptor()); 7354 BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor()); 7355 byte[] buffer = new byte[8192]; // will grow when needed 7356 while (in.readNextHeader()) { 7357 final String key = in.getKey(); 7358 final int size = in.getDataSize(); 7359 7360 // is this a special key? 7361 if (key.equals(KEY_WIDGET_STATE)) { 7362 if (DEBUG) { 7363 Slog.i(TAG, "Restoring widget state for " + packageName); 7364 } 7365 mWidgetData = new byte[size]; 7366 in.readEntityData(mWidgetData, 0, size); 7367 } else { 7368 if (size > buffer.length) { 7369 buffer = new byte[size]; 7370 } 7371 in.readEntityData(buffer, 0, size); 7372 out.writeEntityHeader(key, size); 7373 out.writeEntityData(buffer, size); 7374 } 7375 } 7376 7377 mBackupData.close(); 7378 } 7379 7380 // Okay, we have the data. Now have the agent do the restore. 7381 stage.close(); 7382 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 7383 ParcelFileDescriptor.MODE_READ_ONLY); 7384 7385 mNewState = ParcelFileDescriptor.open(mNewStateName, 7386 ParcelFileDescriptor.MODE_READ_WRITE | 7387 ParcelFileDescriptor.MODE_CREATE | 7388 ParcelFileDescriptor.MODE_TRUNCATE); 7389 7390 // Kick off the restore, checking for hung agents. The timeout or 7391 // the operationComplete() callback will schedule the next step, 7392 // so we do not do that here. 7393 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this); 7394 mAgent.doRestore(mBackupData, appVersionCode, mNewState, 7395 token, mBackupManagerBinder); 7396 } catch (Exception e) { 7397 Slog.e(TAG, "Unable to call app for restore: " + packageName, e); 7398 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7399 packageName, e.toString()); 7400 keyValueAgentErrorCleanup(); // clears any pending timeout messages as well 7401 7402 // After a restore failure we go back to running the queue. If there 7403 // are no more packages to be restored that will be handled by the 7404 // next step. 7405 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7406 } 7407 } 7408 7409 // state RESTORE_FULL : restore one package via streaming engine 7410 private void restoreFull() { 7411 // None of this can run on the work looper here, so we spin asynchronous 7412 // work like this: 7413 // 7414 // StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk() 7415 // write it into the pipe to the engine 7416 // EngineThread: FullRestoreEngine thread communicating with the target app 7417 // 7418 // When finished, StreamFeederThread executes next state as appropriate on the 7419 // backup looper, and the overall unified restore task resumes 7420 try { 7421 StreamFeederThread feeder = new StreamFeederThread(); 7422 if (DEBUG) { 7423 Slog.i(TAG, "Spinning threads for stream restore of " 7424 + mCurrentPackage.packageName); 7425 } 7426 new Thread(feeder, "unified-stream-feeder").start(); 7427 7428 // At this point the feeder is responsible for advancing the restore 7429 // state, so we're done here. 7430 } catch (IOException e) { 7431 // Unable to instantiate the feeder thread -- we need to bail on the 7432 // current target. We haven't asked the transport for data yet, though, 7433 // so we can do that simply by going back to running the restore queue. 7434 Slog.e(TAG, "Unable to construct pipes for stream restore!"); 7435 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7436 } 7437 } 7438 7439 // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end 7440 private void restoreFinished() { 7441 try { 7442 final int token = generateToken(); 7443 prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this); 7444 mAgent.doRestoreFinished(token, mBackupManagerBinder); 7445 // If we get this far, the callback or timeout will schedule the 7446 // next restore state, so we're done 7447 } catch (Exception e) { 7448 Slog.e(TAG, "Unable to finalize restore of " + mCurrentPackage.packageName); 7449 executeNextState(UnifiedRestoreState.FINAL); 7450 } 7451 } 7452 7453 class StreamFeederThread extends RestoreEngine implements Runnable { 7454 final String TAG = "StreamFeederThread"; 7455 FullRestoreEngine mEngine; 7456 7457 // pipe through which we read data from the transport. [0] read, [1] write 7458 ParcelFileDescriptor[] mTransportPipes; 7459 7460 // pipe through which the engine will read data. [0] read, [1] write 7461 ParcelFileDescriptor[] mEnginePipes; 7462 7463 public StreamFeederThread() throws IOException { 7464 mTransportPipes = ParcelFileDescriptor.createPipe(); 7465 mEnginePipes = ParcelFileDescriptor.createPipe(); 7466 setRunning(true); 7467 } 7468 7469 @Override 7470 public void run() { 7471 UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE; 7472 int status = BackupTransport.TRANSPORT_OK; 7473 7474 EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE, 7475 mCurrentPackage.packageName); 7476 7477 mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false); 7478 EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]); 7479 7480 ParcelFileDescriptor eWriteEnd = mEnginePipes[1]; 7481 ParcelFileDescriptor tReadEnd = mTransportPipes[0]; 7482 ParcelFileDescriptor tWriteEnd = mTransportPipes[1]; 7483 7484 int bufferSize = 32 * 1024; 7485 byte[] buffer = new byte[bufferSize]; 7486 FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor()); 7487 FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor()); 7488 7489 // spin up the engine and start moving data to it 7490 new Thread(eThread, "unified-restore-engine").start(); 7491 7492 try { 7493 while (status == BackupTransport.TRANSPORT_OK) { 7494 // have the transport write some of the restoring data to us 7495 int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd); 7496 if (result > 0) { 7497 // The transport wrote this many bytes of restore data to the 7498 // pipe, so pass it along to the engine. 7499 if (MORE_DEBUG) { 7500 Slog.v(TAG, " <- transport provided chunk size " + result); 7501 } 7502 if (result > bufferSize) { 7503 bufferSize = result; 7504 buffer = new byte[bufferSize]; 7505 } 7506 int toCopy = result; 7507 while (toCopy > 0) { 7508 int n = transportIn.read(buffer, 0, toCopy); 7509 engineOut.write(buffer, 0, n); 7510 toCopy -= n; 7511 if (MORE_DEBUG) { 7512 Slog.v(TAG, " -> wrote " + n + " to engine, left=" + toCopy); 7513 } 7514 } 7515 } else if (result == BackupTransport.NO_MORE_DATA) { 7516 // Clean finish. Wind up and we're done! 7517 if (MORE_DEBUG) { 7518 Slog.i(TAG, "Got clean full-restore EOF for " 7519 + mCurrentPackage.packageName); 7520 } 7521 status = BackupTransport.TRANSPORT_OK; 7522 break; 7523 } else { 7524 // Transport reported some sort of failure; the fall-through 7525 // handling will deal properly with that. 7526 Slog.e(TAG, "Error " + result + " streaming restore for " 7527 + mCurrentPackage.packageName); 7528 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7529 status = result; 7530 } 7531 } 7532 if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through"); 7533 } catch (IOException e) { 7534 // We lost our ability to communicate via the pipes. That's worrying 7535 // but potentially recoverable; abandon this package's restore but 7536 // carry on with the next restore target. 7537 Slog.e(TAG, "Unable to route data for restore"); 7538 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7539 mCurrentPackage.packageName, "I/O error on pipes"); 7540 status = BackupTransport.AGENT_ERROR; 7541 } catch (RemoteException e) { 7542 // The transport went away; terminate the whole operation. Closing 7543 // the sockets will wake up the engine and it will then tidy up the 7544 // remote end. 7545 Slog.e(TAG, "Transport failed during restore"); 7546 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7547 status = BackupTransport.TRANSPORT_ERROR; 7548 } finally { 7549 // Close the transport pipes and *our* end of the engine pipe, 7550 // but leave the engine thread's end open so that it properly 7551 // hits EOF and winds up its operations. 7552 IoUtils.closeQuietly(mEnginePipes[1]); 7553 IoUtils.closeQuietly(mTransportPipes[0]); 7554 IoUtils.closeQuietly(mTransportPipes[1]); 7555 7556 // Don't proceed until the engine has torn down the agent etc 7557 eThread.waitForResult(); 7558 7559 if (MORE_DEBUG) { 7560 Slog.i(TAG, "engine thread finished; proceeding"); 7561 } 7562 7563 // Now we're really done with this one too 7564 IoUtils.closeQuietly(mEnginePipes[0]); 7565 7566 // If we hit a transport-level error, we are done with everything; 7567 // if we hit an agent error we just go back to running the queue. 7568 if (status == BackupTransport.TRANSPORT_OK) { 7569 // Clean finish, so just carry on 7570 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7571 } else { 7572 // Something went wrong somewhere. Whether it was at the transport 7573 // level is immaterial; we need to tell the transport to bail 7574 try { 7575 mTransport.abortFullRestore(); 7576 } catch (RemoteException e) { 7577 // transport itself is dead; make sure we handle this as a 7578 // fatal error 7579 status = BackupTransport.TRANSPORT_ERROR; 7580 } 7581 7582 // We also need to wipe the current target's data, as it's probably 7583 // in an incoherent state. 7584 clearApplicationDataSynchronous(mCurrentPackage.packageName); 7585 7586 // Schedule the next state based on the nature of our failure 7587 if (status == BackupTransport.TRANSPORT_ERROR) { 7588 nextState = UnifiedRestoreState.FINAL; 7589 } else { 7590 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7591 } 7592 } 7593 executeNextState(nextState); 7594 setRunning(false); 7595 } 7596 } 7597 7598 } 7599 7600 class EngineThread implements Runnable { 7601 FullRestoreEngine mEngine; 7602 FileInputStream mEngineStream; 7603 7604 EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) { 7605 mEngine = engine; 7606 engine.setRunning(true); 7607 mEngineStream = new FileInputStream(engineSocket.getFileDescriptor()); 7608 } 7609 7610 public boolean isRunning() { 7611 return mEngine.isRunning(); 7612 } 7613 7614 public int waitForResult() { 7615 return mEngine.waitForResult(); 7616 } 7617 7618 @Override 7619 public void run() { 7620 while (mEngine.isRunning()) { 7621 mEngine.restoreOneFile(mEngineStream); 7622 } 7623 } 7624 } 7625 7626 // state FINAL : tear everything down and we're done. 7627 private void finalizeRestore() { 7628 if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver); 7629 7630 try { 7631 mTransport.finishRestore(); 7632 } catch (Exception e) { 7633 Slog.e(TAG, "Error finishing restore", e); 7634 } 7635 7636 // Tell the observer we're done 7637 if (mObserver != null) { 7638 try { 7639 mObserver.restoreFinished(mStatus); 7640 } catch (RemoteException e) { 7641 Slog.d(TAG, "Restore observer died at restoreFinished"); 7642 } 7643 } 7644 7645 // Clear any ongoing session timeout. 7646 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 7647 7648 // If we have a PM token, we must under all circumstances be sure to 7649 // handshake when we've finished. 7650 if (mPmToken > 0) { 7651 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken); 7652 try { 7653 mPackageManagerBinder.finishPackageInstall(mPmToken); 7654 } catch (RemoteException e) { /* can't happen */ } 7655 } else { 7656 // We were invoked via an active restore session, not by the Package 7657 // Manager, so start up the session timeout again. 7658 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, 7659 TIMEOUT_RESTORE_INTERVAL); 7660 } 7661 7662 // Kick off any work that may be needed regarding app widget restores 7663 AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER); 7664 7665 // If this was a full-system restore, record the ancestral 7666 // dataset information 7667 if (mIsSystemRestore && mPmAgent != null) { 7668 mAncestralPackages = mPmAgent.getRestoredPackages(); 7669 mAncestralToken = mToken; 7670 writeRestoreTokens(); 7671 } 7672 7673 // done; we can finally release the wakelock and be legitimately done. 7674 Slog.i(TAG, "Restore complete."); 7675 mWakelock.release(); 7676 } 7677 7678 void keyValueAgentErrorCleanup() { 7679 // If the agent fails restore, it might have put the app's data 7680 // into an incoherent state. For consistency we wipe its data 7681 // again in this case before continuing with normal teardown 7682 clearApplicationDataSynchronous(mCurrentPackage.packageName); 7683 keyValueAgentCleanup(); 7684 } 7685 7686 void keyValueAgentCleanup() { 7687 mBackupDataName.delete(); 7688 mStageName.delete(); 7689 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 7690 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 7691 mBackupData = mNewState = null; 7692 7693 // if everything went okay, remember the recorded state now 7694 // 7695 // !!! TODO: the restored data could be migrated on the server 7696 // side into the current dataset. In that case the new state file 7697 // we just created would reflect the data already extant in the 7698 // backend, so there'd be nothing more to do. Until that happens, 7699 // however, we need to make sure that we record the data to the 7700 // current backend dataset. (Yes, this means shipping the data over 7701 // the wire in both directions. That's bad, but consistency comes 7702 // first, then efficiency.) Once we introduce server-side data 7703 // migration to the newly-restored device's dataset, we will change 7704 // the following from a discard of the newly-written state to the 7705 // "correct" operation of renaming into the canonical state blob. 7706 mNewStateName.delete(); // TODO: remove; see above comment 7707 //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this 7708 7709 // If this wasn't the PM pseudopackage, tear down the agent side 7710 if (mCurrentPackage.applicationInfo != null) { 7711 // unbind and tidy up even on timeout or failure 7712 try { 7713 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 7714 7715 // The agent was probably running with a stub Application object, 7716 // which isn't a valid run mode for the main app logic. Shut 7717 // down the app so that next time it's launched, it gets the 7718 // usual full initialization. Note that this is only done for 7719 // full-system restores: when a single app has requested a restore, 7720 // it is explicitly not killed following that operation. 7721 if (mTargetPackage == null && (mCurrentPackage.applicationInfo.flags 7722 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) { 7723 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of " 7724 + mCurrentPackage.applicationInfo.processName); 7725 mActivityManager.killApplicationProcess( 7726 mCurrentPackage.applicationInfo.processName, 7727 mCurrentPackage.applicationInfo.uid); 7728 } 7729 } catch (RemoteException e) { 7730 // can't happen; we run in the same process as the activity manager 7731 } 7732 } 7733 7734 // The caller is responsible for reestablishing the state machine; our 7735 // responsibility here is to clear the decks for whatever comes next. 7736 mBackupHandler.removeMessages(MSG_TIMEOUT, this); 7737 synchronized (mCurrentOpLock) { 7738 mCurrentOperations.clear(); 7739 } 7740 } 7741 7742 @Override 7743 public void operationComplete() { 7744 if (MORE_DEBUG) { 7745 Slog.i(TAG, "operationComplete() during restore: target=" 7746 + mCurrentPackage.packageName 7747 + " state=" + mState); 7748 } 7749 7750 final UnifiedRestoreState nextState; 7751 switch (mState) { 7752 case INITIAL: 7753 // We've just (manually) restored the PMBA. It doesn't need the 7754 // additional restore-finished callback so we bypass that and go 7755 // directly to running the queue. 7756 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7757 break; 7758 7759 case RESTORE_KEYVALUE: 7760 case RESTORE_FULL: { 7761 // Okay, we've just heard back from the agent that it's done with 7762 // the restore itself. We now have to send the same agent its 7763 // doRestoreFinished() callback, so roll into that state. 7764 nextState = UnifiedRestoreState.RESTORE_FINISHED; 7765 break; 7766 } 7767 7768 case RESTORE_FINISHED: { 7769 // Okay, we're done with this package. Tidy up and go on to the next 7770 // app in the queue. 7771 int size = (int) mBackupDataName.length(); 7772 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, 7773 mCurrentPackage.packageName, size); 7774 7775 // Just go back to running the restore queue 7776 keyValueAgentCleanup(); 7777 7778 // If there was widget state associated with this app, get the OS to 7779 // incorporate it into current bookeeping and then pass that along to 7780 // the app as part of the restore-time work. 7781 if (mWidgetData != null) { 7782 restoreWidgetData(mCurrentPackage.packageName, mWidgetData); 7783 } 7784 7785 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7786 break; 7787 } 7788 7789 default: { 7790 // Some kind of horrible semantic error; we're in an unexpected state. 7791 // Back off hard and wind up. 7792 Slog.e(TAG, "Unexpected restore callback into state " + mState); 7793 keyValueAgentErrorCleanup(); 7794 nextState = UnifiedRestoreState.FINAL; 7795 break; 7796 } 7797 } 7798 7799 executeNextState(nextState); 7800 } 7801 7802 // A call to agent.doRestore() or agent.doRestoreFinished() has timed out 7803 @Override 7804 public void handleTimeout() { 7805 Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName); 7806 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7807 mCurrentPackage.packageName, "restore timeout"); 7808 // Handle like an agent that threw on invocation: wipe it and go on to the next 7809 keyValueAgentErrorCleanup(); 7810 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7811 } 7812 7813 void executeNextState(UnifiedRestoreState nextState) { 7814 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 7815 + this + " nextState=" + nextState); 7816 mState = nextState; 7817 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 7818 mBackupHandler.sendMessage(msg); 7819 } 7820 7821 // restore observer support 7822 void sendStartRestore(int numPackages) { 7823 if (mObserver != null) { 7824 try { 7825 mObserver.restoreStarting(numPackages); 7826 } catch (RemoteException e) { 7827 Slog.w(TAG, "Restore observer went away: startRestore"); 7828 mObserver = null; 7829 } 7830 } 7831 } 7832 7833 void sendOnRestorePackage(String name) { 7834 if (mObserver != null) { 7835 if (mObserver != null) { 7836 try { 7837 mObserver.onUpdate(mCount, name); 7838 } catch (RemoteException e) { 7839 Slog.d(TAG, "Restore observer died in onUpdate"); 7840 mObserver = null; 7841 } 7842 } 7843 } 7844 } 7845 7846 void sendEndRestore() { 7847 if (mObserver != null) { 7848 try { 7849 mObserver.restoreFinished(mStatus); 7850 } catch (RemoteException e) { 7851 Slog.w(TAG, "Restore observer went away: endRestore"); 7852 mObserver = null; 7853 } 7854 } 7855 } 7856 } 7857 7858 class PerformClearTask implements Runnable { 7859 IBackupTransport mTransport; 7860 PackageInfo mPackage; 7861 7862 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) { 7863 mTransport = transport; 7864 mPackage = packageInfo; 7865 } 7866 7867 public void run() { 7868 try { 7869 // Clear the on-device backup state to ensure a full backup next time 7870 File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); 7871 File stateFile = new File(stateDir, mPackage.packageName); 7872 stateFile.delete(); 7873 7874 // Tell the transport to remove all the persistent storage for the app 7875 // TODO - need to handle failures 7876 mTransport.clearBackupData(mPackage); 7877 } catch (RemoteException e) { 7878 // can't happen; the transport is local 7879 } catch (Exception e) { 7880 Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage); 7881 } finally { 7882 try { 7883 // TODO - need to handle failures 7884 mTransport.finishBackup(); 7885 } catch (RemoteException e) { 7886 // can't happen; the transport is local 7887 } 7888 7889 // Last but not least, release the cpu 7890 mWakelock.release(); 7891 } 7892 } 7893 } 7894 7895 class PerformInitializeTask implements Runnable { 7896 HashSet<String> mQueue; 7897 7898 PerformInitializeTask(HashSet<String> transportNames) { 7899 mQueue = transportNames; 7900 } 7901 7902 public void run() { 7903 try { 7904 for (String transportName : mQueue) { 7905 IBackupTransport transport = getTransport(transportName); 7906 if (transport == null) { 7907 Slog.e(TAG, "Requested init for " + transportName + " but not found"); 7908 continue; 7909 } 7910 7911 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); 7912 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName()); 7913 long startRealtime = SystemClock.elapsedRealtime(); 7914 int status = transport.initializeDevice(); 7915 7916 if (status == BackupTransport.TRANSPORT_OK) { 7917 status = transport.finishBackup(); 7918 } 7919 7920 // Okay, the wipe really happened. Clean up our local bookkeeping. 7921 if (status == BackupTransport.TRANSPORT_OK) { 7922 Slog.i(TAG, "Device init successful"); 7923 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); 7924 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 7925 resetBackupState(new File(mBaseStateDir, transport.transportDirName())); 7926 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); 7927 synchronized (mQueueLock) { 7928 recordInitPendingLocked(false, transportName); 7929 } 7930 } else { 7931 // If this didn't work, requeue this one and try again 7932 // after a suitable interval 7933 Slog.e(TAG, "Transport error in initializeDevice()"); 7934 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 7935 synchronized (mQueueLock) { 7936 recordInitPendingLocked(true, transportName); 7937 } 7938 // do this via another alarm to make sure of the wakelock states 7939 long delay = transport.requestBackupTime(); 7940 if (DEBUG) Slog.w(TAG, "init failed on " 7941 + transportName + " resched in " + delay); 7942 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 7943 System.currentTimeMillis() + delay, mRunInitIntent); 7944 } 7945 } 7946 } catch (RemoteException e) { 7947 // can't happen; the transports are local 7948 } catch (Exception e) { 7949 Slog.e(TAG, "Unexpected error performing init", e); 7950 } finally { 7951 // Done; release the wakelock 7952 mWakelock.release(); 7953 } 7954 } 7955 } 7956 7957 private void dataChangedImpl(String packageName) { 7958 HashSet<String> targets = dataChangedTargets(packageName); 7959 dataChangedImpl(packageName, targets); 7960 } 7961 7962 private void dataChangedImpl(String packageName, HashSet<String> targets) { 7963 // Record that we need a backup pass for the caller. Since multiple callers 7964 // may share a uid, we need to note all candidates within that uid and schedule 7965 // a backup pass for each of them. 7966 EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName); 7967 7968 if (targets == null) { 7969 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 7970 + " uid=" + Binder.getCallingUid()); 7971 return; 7972 } 7973 7974 synchronized (mQueueLock) { 7975 // Note that this client has made data changes that need to be backed up 7976 if (targets.contains(packageName)) { 7977 // Add the caller to the set of pending backups. If there is 7978 // one already there, then overwrite it, but no harm done. 7979 BackupRequest req = new BackupRequest(packageName); 7980 if (mPendingBackups.put(packageName, req) == null) { 7981 if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName); 7982 7983 // Journal this request in case of crash. The put() 7984 // operation returned null when this package was not already 7985 // in the set; we want to avoid touching the disk redundantly. 7986 writeToJournalLocked(packageName); 7987 7988 if (MORE_DEBUG) { 7989 int numKeys = mPendingBackups.size(); 7990 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); 7991 for (BackupRequest b : mPendingBackups.values()) { 7992 Slog.d(TAG, " + " + b); 7993 } 7994 } 7995 } 7996 } 7997 } 7998 } 7999 8000 // Note: packageName is currently unused, but may be in the future 8001 private HashSet<String> dataChangedTargets(String packageName) { 8002 // If the caller does not hold the BACKUP permission, it can only request a 8003 // backup of its own data. 8004 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 8005 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 8006 synchronized (mBackupParticipants) { 8007 return mBackupParticipants.get(Binder.getCallingUid()); 8008 } 8009 } 8010 8011 // a caller with full permission can ask to back up any participating app 8012 // !!! TODO: allow backup of ANY app? 8013 HashSet<String> targets = new HashSet<String>(); 8014 synchronized (mBackupParticipants) { 8015 int N = mBackupParticipants.size(); 8016 for (int i = 0; i < N; i++) { 8017 HashSet<String> s = mBackupParticipants.valueAt(i); 8018 if (s != null) { 8019 targets.addAll(s); 8020 } 8021 } 8022 } 8023 return targets; 8024 } 8025 8026 private void writeToJournalLocked(String str) { 8027 RandomAccessFile out = null; 8028 try { 8029 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir); 8030 out = new RandomAccessFile(mJournal, "rws"); 8031 out.seek(out.length()); 8032 out.writeUTF(str); 8033 } catch (IOException e) { 8034 Slog.e(TAG, "Can't write " + str + " to backup journal", e); 8035 mJournal = null; 8036 } finally { 8037 try { if (out != null) out.close(); } catch (IOException e) {} 8038 } 8039 } 8040 8041 // ----- IBackupManager binder interface ----- 8042 8043 public void dataChanged(final String packageName) { 8044 final int callingUserHandle = UserHandle.getCallingUserId(); 8045 if (callingUserHandle != UserHandle.USER_OWNER) { 8046 // App is running under a non-owner user profile. For now, we do not back 8047 // up data from secondary user profiles. 8048 // TODO: backups for all user profiles. 8049 if (MORE_DEBUG) { 8050 Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " 8051 + callingUserHandle); 8052 } 8053 return; 8054 } 8055 8056 final HashSet<String> targets = dataChangedTargets(packageName); 8057 if (targets == null) { 8058 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 8059 + " uid=" + Binder.getCallingUid()); 8060 return; 8061 } 8062 8063 mBackupHandler.post(new Runnable() { 8064 public void run() { 8065 dataChangedImpl(packageName, targets); 8066 } 8067 }); 8068 } 8069 8070 // Clear the given package's backup data from the current transport 8071 public void clearBackupData(String transportName, String packageName) { 8072 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); 8073 PackageInfo info; 8074 try { 8075 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 8076 } catch (NameNotFoundException e) { 8077 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); 8078 return; 8079 } 8080 8081 // If the caller does not hold the BACKUP permission, it can only request a 8082 // wipe of its own backed-up data. 8083 HashSet<String> apps; 8084 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 8085 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 8086 apps = mBackupParticipants.get(Binder.getCallingUid()); 8087 } else { 8088 // a caller with full permission can ask to back up any participating app 8089 // !!! TODO: allow data-clear of ANY app? 8090 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps"); 8091 apps = new HashSet<String>(); 8092 int N = mBackupParticipants.size(); 8093 for (int i = 0; i < N; i++) { 8094 HashSet<String> s = mBackupParticipants.valueAt(i); 8095 if (s != null) { 8096 apps.addAll(s); 8097 } 8098 } 8099 } 8100 8101 // Is the given app an available participant? 8102 if (apps.contains(packageName)) { 8103 // found it; fire off the clear request 8104 if (DEBUG) Slog.v(TAG, "Found the app - running clear process"); 8105 mBackupHandler.removeMessages(MSG_RETRY_CLEAR); 8106 synchronized (mQueueLock) { 8107 final IBackupTransport transport = getTransport(transportName); 8108 if (transport == null) { 8109 // transport is currently unavailable -- make sure to retry 8110 Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR, 8111 new ClearRetryParams(transportName, packageName)); 8112 mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL); 8113 return; 8114 } 8115 long oldId = Binder.clearCallingIdentity(); 8116 mWakelock.acquire(); 8117 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, 8118 new ClearParams(transport, info)); 8119 mBackupHandler.sendMessage(msg); 8120 Binder.restoreCallingIdentity(oldId); 8121 } 8122 } 8123 } 8124 8125 // Run a backup pass immediately for any applications that have declared 8126 // that they have pending updates. 8127 public void backupNow() { 8128 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow"); 8129 8130 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); 8131 synchronized (mQueueLock) { 8132 // Because the alarms we are using can jitter, and we want an *immediate* 8133 // backup pass to happen, we restart the timer beginning with "next time," 8134 // then manually fire the backup trigger intent ourselves. 8135 startBackupAlarmsLocked(BACKUP_INTERVAL); 8136 try { 8137 mRunBackupIntent.send(); 8138 } catch (PendingIntent.CanceledException e) { 8139 // should never happen 8140 Slog.e(TAG, "run-backup intent cancelled!"); 8141 } 8142 } 8143 } 8144 8145 boolean deviceIsProvisioned() { 8146 final ContentResolver resolver = mContext.getContentResolver(); 8147 return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); 8148 } 8149 8150 // Run a *full* backup pass for the given packages, writing the resulting data stream 8151 // to the supplied file descriptor. This method is synchronous and does not return 8152 // to the caller until the backup has been completed. 8153 // 8154 // This is the variant used by 'adb backup'; it requires on-screen confirmation 8155 // by the user because it can be used to offload data over untrusted USB. 8156 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, 8157 boolean includeObbs, boolean includeShared, boolean doWidgets, 8158 boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) { 8159 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); 8160 8161 final int callingUserHandle = UserHandle.getCallingUserId(); 8162 if (callingUserHandle != UserHandle.USER_OWNER) { 8163 throw new IllegalStateException("Backup supported only for the device owner"); 8164 } 8165 8166 // Validate 8167 if (!doAllApps) { 8168 if (!includeShared) { 8169 // If we're backing up shared data (sdcard or equivalent), then we can run 8170 // without any supplied app names. Otherwise, we'd be doing no work, so 8171 // report the error. 8172 if (pkgList == null || pkgList.length == 0) { 8173 throw new IllegalArgumentException( 8174 "Backup requested but neither shared nor any apps named"); 8175 } 8176 } 8177 } 8178 8179 long oldId = Binder.clearCallingIdentity(); 8180 try { 8181 // Doesn't make sense to do a full backup prior to setup 8182 if (!deviceIsProvisioned()) { 8183 Slog.i(TAG, "Full backup not supported before setup"); 8184 return; 8185 } 8186 8187 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks 8188 + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps 8189 + " system=" + includeSystem + " pkgs=" + pkgList); 8190 Slog.i(TAG, "Beginning full backup..."); 8191 8192 FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs, 8193 includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList); 8194 final int token = generateToken(); 8195 synchronized (mFullConfirmations) { 8196 mFullConfirmations.put(token, params); 8197 } 8198 8199 // start up the confirmation UI 8200 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token); 8201 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { 8202 Slog.e(TAG, "Unable to launch full backup confirmation"); 8203 mFullConfirmations.delete(token); 8204 return; 8205 } 8206 8207 // make sure the screen is lit for the user interaction 8208 mPowerManager.userActivity(SystemClock.uptimeMillis(), false); 8209 8210 // start the confirmation countdown 8211 startConfirmationTimeout(token, params); 8212 8213 // wait for the backup to be performed 8214 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion..."); 8215 waitForCompletion(params); 8216 } finally { 8217 try { 8218 fd.close(); 8219 } catch (IOException e) { 8220 // just eat it 8221 } 8222 Binder.restoreCallingIdentity(oldId); 8223 Slog.d(TAG, "Full backup processing complete."); 8224 } 8225 } 8226 8227 public void fullTransportBackup(String[] pkgNames) { 8228 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, 8229 "fullTransportBackup"); 8230 8231 final int callingUserHandle = UserHandle.getCallingUserId(); 8232 if (callingUserHandle != UserHandle.USER_OWNER) { 8233 throw new IllegalStateException("Restore supported only for the device owner"); 8234 } 8235 8236 if (DEBUG) { 8237 Slog.d(TAG, "fullTransportBackup()"); 8238 } 8239 8240 AtomicBoolean latch = new AtomicBoolean(false); 8241 PerformFullTransportBackupTask task = 8242 new PerformFullTransportBackupTask(null, pkgNames, false, null, latch); 8243 (new Thread(task, "full-transport-master")).start(); 8244 synchronized (latch) { 8245 try { 8246 while (latch.get() == false) { 8247 latch.wait(); 8248 } 8249 } catch (InterruptedException e) {} 8250 } 8251 if (DEBUG) { 8252 Slog.d(TAG, "Done with full transport backup."); 8253 } 8254 } 8255 8256 public void fullRestore(ParcelFileDescriptor fd) { 8257 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); 8258 8259 final int callingUserHandle = UserHandle.getCallingUserId(); 8260 if (callingUserHandle != UserHandle.USER_OWNER) { 8261 throw new IllegalStateException("Restore supported only for the device owner"); 8262 } 8263 8264 long oldId = Binder.clearCallingIdentity(); 8265 8266 try { 8267 // Check whether the device has been provisioned -- we don't handle 8268 // full restores prior to completing the setup process. 8269 if (!deviceIsProvisioned()) { 8270 Slog.i(TAG, "Full restore not permitted before setup"); 8271 return; 8272 } 8273 8274 Slog.i(TAG, "Beginning full restore..."); 8275 8276 FullRestoreParams params = new FullRestoreParams(fd); 8277 final int token = generateToken(); 8278 synchronized (mFullConfirmations) { 8279 mFullConfirmations.put(token, params); 8280 } 8281 8282 // start up the confirmation UI 8283 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token); 8284 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) { 8285 Slog.e(TAG, "Unable to launch full restore confirmation"); 8286 mFullConfirmations.delete(token); 8287 return; 8288 } 8289 8290 // make sure the screen is lit for the user interaction 8291 mPowerManager.userActivity(SystemClock.uptimeMillis(), false); 8292 8293 // start the confirmation countdown 8294 startConfirmationTimeout(token, params); 8295 8296 // wait for the restore to be performed 8297 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion..."); 8298 waitForCompletion(params); 8299 } finally { 8300 try { 8301 fd.close(); 8302 } catch (IOException e) { 8303 Slog.w(TAG, "Error trying to close fd after full restore: " + e); 8304 } 8305 Binder.restoreCallingIdentity(oldId); 8306 Slog.i(TAG, "Full restore processing complete."); 8307 } 8308 } 8309 8310 boolean startConfirmationUi(int token, String action) { 8311 try { 8312 Intent confIntent = new Intent(action); 8313 confIntent.setClassName("com.android.backupconfirm", 8314 "com.android.backupconfirm.BackupRestoreConfirmation"); 8315 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token); 8316 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 8317 mContext.startActivity(confIntent); 8318 } catch (ActivityNotFoundException e) { 8319 return false; 8320 } 8321 return true; 8322 } 8323 8324 void startConfirmationTimeout(int token, FullParams params) { 8325 if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after " 8326 + TIMEOUT_FULL_CONFIRMATION + " millis"); 8327 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, 8328 token, 0, params); 8329 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION); 8330 } 8331 8332 void waitForCompletion(FullParams params) { 8333 synchronized (params.latch) { 8334 while (params.latch.get() == false) { 8335 try { 8336 params.latch.wait(); 8337 } catch (InterruptedException e) { /* never interrupted */ } 8338 } 8339 } 8340 } 8341 8342 void signalFullBackupRestoreCompletion(FullParams params) { 8343 synchronized (params.latch) { 8344 params.latch.set(true); 8345 params.latch.notifyAll(); 8346 } 8347 } 8348 8349 // Confirm that the previously-requested full backup/restore operation can proceed. This 8350 // is used to require a user-facing disclosure about the operation. 8351 public void acknowledgeFullBackupOrRestore(int token, boolean allow, 8352 String curPassword, String encPpassword, IFullBackupRestoreObserver observer) { 8353 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token 8354 + " allow=" + allow); 8355 8356 // TODO: possibly require not just this signature-only permission, but even 8357 // require that the specific designated confirmation-UI app uid is the caller? 8358 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore"); 8359 8360 long oldId = Binder.clearCallingIdentity(); 8361 try { 8362 8363 FullParams params; 8364 synchronized (mFullConfirmations) { 8365 params = mFullConfirmations.get(token); 8366 if (params != null) { 8367 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params); 8368 mFullConfirmations.delete(token); 8369 8370 if (allow) { 8371 final int verb = params instanceof FullBackupParams 8372 ? MSG_RUN_ADB_BACKUP 8373 : MSG_RUN_ADB_RESTORE; 8374 8375 params.observer = observer; 8376 params.curPassword = curPassword; 8377 8378 params.encryptPassword = encPpassword; 8379 8380 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb); 8381 mWakelock.acquire(); 8382 Message msg = mBackupHandler.obtainMessage(verb, params); 8383 mBackupHandler.sendMessage(msg); 8384 } else { 8385 Slog.w(TAG, "User rejected full backup/restore operation"); 8386 // indicate completion without having actually transferred any data 8387 signalFullBackupRestoreCompletion(params); 8388 } 8389 } else { 8390 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token"); 8391 } 8392 } 8393 } finally { 8394 Binder.restoreCallingIdentity(oldId); 8395 } 8396 } 8397 8398 // Enable/disable backups 8399 public void setBackupEnabled(boolean enable) { 8400 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8401 "setBackupEnabled"); 8402 8403 Slog.i(TAG, "Backup enabled => " + enable); 8404 8405 long oldId = Binder.clearCallingIdentity(); 8406 try { 8407 boolean wasEnabled = mEnabled; 8408 synchronized (this) { 8409 Settings.Secure.putInt(mContext.getContentResolver(), 8410 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0); 8411 mEnabled = enable; 8412 } 8413 8414 synchronized (mQueueLock) { 8415 if (enable && !wasEnabled && mProvisioned) { 8416 // if we've just been enabled, start scheduling backup passes 8417 startBackupAlarmsLocked(BACKUP_INTERVAL); 8418 scheduleNextFullBackupJob(); 8419 } else if (!enable) { 8420 // No longer enabled, so stop running backups 8421 if (DEBUG) Slog.i(TAG, "Opting out of backup"); 8422 8423 mAlarmManager.cancel(mRunBackupIntent); 8424 8425 // This also constitutes an opt-out, so we wipe any data for 8426 // this device from the backend. We start that process with 8427 // an alarm in order to guarantee wakelock states. 8428 if (wasEnabled && mProvisioned) { 8429 // NOTE: we currently flush every registered transport, not just 8430 // the currently-active one. 8431 HashSet<String> allTransports; 8432 synchronized (mTransports) { 8433 allTransports = new HashSet<String>(mTransports.keySet()); 8434 } 8435 // build the set of transports for which we are posting an init 8436 for (String transport : allTransports) { 8437 recordInitPendingLocked(true, transport); 8438 } 8439 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 8440 mRunInitIntent); 8441 } 8442 } 8443 } 8444 } finally { 8445 Binder.restoreCallingIdentity(oldId); 8446 } 8447 } 8448 8449 // Enable/disable automatic restore of app data at install time 8450 public void setAutoRestore(boolean doAutoRestore) { 8451 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8452 "setAutoRestore"); 8453 8454 Slog.i(TAG, "Auto restore => " + doAutoRestore); 8455 8456 final long oldId = Binder.clearCallingIdentity(); 8457 try { 8458 synchronized (this) { 8459 Settings.Secure.putInt(mContext.getContentResolver(), 8460 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0); 8461 mAutoRestore = doAutoRestore; 8462 } 8463 } finally { 8464 Binder.restoreCallingIdentity(oldId); 8465 } 8466 } 8467 8468 // Mark the backup service as having been provisioned 8469 public void setBackupProvisioned(boolean available) { 8470 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8471 "setBackupProvisioned"); 8472 /* 8473 * This is now a no-op; provisioning is simply the device's own setup state. 8474 */ 8475 } 8476 8477 private void startBackupAlarmsLocked(long delayBeforeFirstBackup) { 8478 // We used to use setInexactRepeating(), but that may be linked to 8479 // backups running at :00 more often than not, creating load spikes. 8480 // Schedule at an exact time for now, and also add a bit of "fuzz". 8481 8482 Random random = new Random(); 8483 long when = System.currentTimeMillis() + delayBeforeFirstBackup + 8484 random.nextInt(FUZZ_MILLIS); 8485 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when, 8486 BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent); 8487 mNextBackupPass = when; 8488 } 8489 8490 // Report whether the backup mechanism is currently enabled 8491 public boolean isBackupEnabled() { 8492 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); 8493 return mEnabled; // no need to synchronize just to read it 8494 } 8495 8496 // Report the name of the currently active transport 8497 public String getCurrentTransport() { 8498 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8499 "getCurrentTransport"); 8500 if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); 8501 return mCurrentTransport; 8502 } 8503 8504 // Report all known, available backup transports 8505 public String[] listAllTransports() { 8506 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); 8507 8508 String[] list = null; 8509 ArrayList<String> known = new ArrayList<String>(); 8510 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { 8511 if (entry.getValue() != null) { 8512 known.add(entry.getKey()); 8513 } 8514 } 8515 8516 if (known.size() > 0) { 8517 list = new String[known.size()]; 8518 known.toArray(list); 8519 } 8520 return list; 8521 } 8522 8523 // Select which transport to use for the next backup operation. 8524 public String selectBackupTransport(String transport) { 8525 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8526 "selectBackupTransport"); 8527 8528 synchronized (mTransports) { 8529 final long oldId = Binder.clearCallingIdentity(); 8530 try { 8531 String prevTransport = mCurrentTransport; 8532 mCurrentTransport = transport; 8533 Settings.Secure.putString(mContext.getContentResolver(), 8534 Settings.Secure.BACKUP_TRANSPORT, transport); 8535 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport 8536 + " returning " + prevTransport); 8537 return prevTransport; 8538 } finally { 8539 Binder.restoreCallingIdentity(oldId); 8540 } 8541 } 8542 } 8543 8544 // Supply the configuration Intent for the given transport. If the name is not one 8545 // of the available transports, or if the transport does not supply any configuration 8546 // UI, the method returns null. 8547 public Intent getConfigurationIntent(String transportName) { 8548 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8549 "getConfigurationIntent"); 8550 8551 synchronized (mTransports) { 8552 final IBackupTransport transport = mTransports.get(transportName); 8553 if (transport != null) { 8554 try { 8555 final Intent intent = transport.configurationIntent(); 8556 if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent " 8557 + intent); 8558 return intent; 8559 } catch (RemoteException e) { 8560 /* fall through to return null */ 8561 } 8562 } 8563 } 8564 8565 return null; 8566 } 8567 8568 // Supply the configuration summary string for the given transport. If the name is 8569 // not one of the available transports, or if the transport does not supply any 8570 // summary / destination string, the method can return null. 8571 // 8572 // This string is used VERBATIM as the summary text of the relevant Settings item! 8573 public String getDestinationString(String transportName) { 8574 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8575 "getDestinationString"); 8576 8577 synchronized (mTransports) { 8578 final IBackupTransport transport = mTransports.get(transportName); 8579 if (transport != null) { 8580 try { 8581 final String text = transport.currentDestinationString(); 8582 if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text); 8583 return text; 8584 } catch (RemoteException e) { 8585 /* fall through to return null */ 8586 } 8587 } 8588 } 8589 8590 return null; 8591 } 8592 8593 // Supply the manage-data intent for the given transport. 8594 public Intent getDataManagementIntent(String transportName) { 8595 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8596 "getDataManagementIntent"); 8597 8598 synchronized (mTransports) { 8599 final IBackupTransport transport = mTransports.get(transportName); 8600 if (transport != null) { 8601 try { 8602 final Intent intent = transport.dataManagementIntent(); 8603 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent " 8604 + intent); 8605 return intent; 8606 } catch (RemoteException e) { 8607 /* fall through to return null */ 8608 } 8609 } 8610 } 8611 8612 return null; 8613 } 8614 8615 // Supply the menu label for affordances that fire the manage-data intent 8616 // for the given transport. 8617 public String getDataManagementLabel(String transportName) { 8618 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8619 "getDataManagementLabel"); 8620 8621 synchronized (mTransports) { 8622 final IBackupTransport transport = mTransports.get(transportName); 8623 if (transport != null) { 8624 try { 8625 final String text = transport.dataManagementLabel(); 8626 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text); 8627 return text; 8628 } catch (RemoteException e) { 8629 /* fall through to return null */ 8630 } 8631 } 8632 } 8633 8634 return null; 8635 } 8636 8637 // Callback: a requested backup agent has been instantiated. This should only 8638 // be called from the Activity Manager. 8639 public void agentConnected(String packageName, IBinder agentBinder) { 8640 synchronized(mAgentConnectLock) { 8641 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 8642 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); 8643 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); 8644 mConnectedAgent = agent; 8645 mConnecting = false; 8646 } else { 8647 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 8648 + " claiming agent connected"); 8649 } 8650 mAgentConnectLock.notifyAll(); 8651 } 8652 } 8653 8654 // Callback: a backup agent has failed to come up, or has unexpectedly quit. 8655 // If the agent failed to come up in the first place, the agentBinder argument 8656 // will be null. This should only be called from the Activity Manager. 8657 public void agentDisconnected(String packageName) { 8658 // TODO: handle backup being interrupted 8659 synchronized(mAgentConnectLock) { 8660 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 8661 mConnectedAgent = null; 8662 mConnecting = false; 8663 } else { 8664 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 8665 + " claiming agent disconnected"); 8666 } 8667 mAgentConnectLock.notifyAll(); 8668 } 8669 } 8670 8671 // An application being installed will need a restore pass, then the Package Manager 8672 // will need to be told when the restore is finished. 8673 public void restoreAtInstall(String packageName, int token) { 8674 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 8675 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 8676 + " attemping install-time restore"); 8677 return; 8678 } 8679 8680 boolean skip = false; 8681 8682 long restoreSet = getAvailableRestoreToken(packageName); 8683 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName 8684 + " token=" + Integer.toHexString(token) 8685 + " restoreSet=" + Long.toHexString(restoreSet)); 8686 if (restoreSet == 0) { 8687 if (MORE_DEBUG) Slog.i(TAG, "No restore set"); 8688 skip = true; 8689 } 8690 8691 // Do we have a transport to fetch data for us? 8692 IBackupTransport transport = getTransport(mCurrentTransport); 8693 if (transport == null) { 8694 if (DEBUG) Slog.w(TAG, "No transport"); 8695 skip = true; 8696 } 8697 8698 if (!mAutoRestore) { 8699 if (DEBUG) { 8700 Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore); 8701 } 8702 skip = true; 8703 } 8704 8705 if (!skip) { 8706 try { 8707 // okay, we're going to attempt a restore of this package from this restore set. 8708 // The eventual message back into the Package Manager to run the post-install 8709 // steps for 'token' will be issued from the restore handling code. 8710 8711 // This can throw and so *must* happen before the wakelock is acquired 8712 String dirName = transport.transportDirName(); 8713 8714 // We can use a synthetic PackageInfo here because: 8715 // 1. We know it's valid, since the Package Manager supplied the name 8716 // 2. Only the packageName field will be used by the restore code 8717 PackageInfo pkg = new PackageInfo(); 8718 pkg.packageName = packageName; 8719 8720 mWakelock.acquire(); 8721 if (MORE_DEBUG) { 8722 Slog.d(TAG, "Restore at install of " + packageName); 8723 } 8724 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 8725 msg.obj = new RestoreParams(transport, dirName, null, 8726 restoreSet, pkg, token); 8727 mBackupHandler.sendMessage(msg); 8728 } catch (RemoteException e) { 8729 // Binding to the transport broke; back off and proceed with the installation. 8730 Slog.e(TAG, "Unable to contact transport"); 8731 skip = true; 8732 } 8733 } 8734 8735 if (skip) { 8736 // Auto-restore disabled or no way to attempt a restore; just tell the Package 8737 // Manager to proceed with the post-install handling for this package. 8738 if (DEBUG) Slog.v(TAG, "Finishing install immediately"); 8739 try { 8740 mPackageManagerBinder.finishPackageInstall(token); 8741 } catch (RemoteException e) { /* can't happen */ } 8742 } 8743 } 8744 8745 // Hand off a restore session 8746 public IRestoreSession beginRestoreSession(String packageName, String transport) { 8747 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName 8748 + " transport=" + transport); 8749 8750 boolean needPermission = true; 8751 if (transport == null) { 8752 transport = mCurrentTransport; 8753 8754 if (packageName != null) { 8755 PackageInfo app = null; 8756 try { 8757 app = mPackageManager.getPackageInfo(packageName, 0); 8758 } catch (NameNotFoundException nnf) { 8759 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 8760 throw new IllegalArgumentException("Package " + packageName + " not found"); 8761 } 8762 8763 if (app.applicationInfo.uid == Binder.getCallingUid()) { 8764 // So: using the current active transport, and the caller has asked 8765 // that its own package will be restored. In this narrow use case 8766 // we do not require the caller to hold the permission. 8767 needPermission = false; 8768 } 8769 } 8770 } 8771 8772 if (needPermission) { 8773 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8774 "beginRestoreSession"); 8775 } else { 8776 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed"); 8777 } 8778 8779 synchronized(this) { 8780 if (mActiveRestoreSession != null) { 8781 Slog.d(TAG, "Restore session requested but one already active"); 8782 return null; 8783 } 8784 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport); 8785 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 8786 } 8787 return mActiveRestoreSession; 8788 } 8789 8790 void clearRestoreSession(ActiveRestoreSession currentSession) { 8791 synchronized(this) { 8792 if (currentSession != mActiveRestoreSession) { 8793 Slog.e(TAG, "ending non-current restore session"); 8794 } else { 8795 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout"); 8796 mActiveRestoreSession = null; 8797 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 8798 } 8799 } 8800 } 8801 8802 // Note that a currently-active backup agent has notified us that it has 8803 // completed the given outstanding asynchronous backup/restore operation. 8804 public void opComplete(int token) { 8805 if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token)); 8806 Operation op = null; 8807 synchronized (mCurrentOpLock) { 8808 op = mCurrentOperations.get(token); 8809 if (op != null) { 8810 op.state = OP_ACKNOWLEDGED; 8811 } 8812 mCurrentOpLock.notifyAll(); 8813 } 8814 8815 // The completion callback, if any, is invoked on the handler 8816 if (op != null && op.callback != null) { 8817 Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback); 8818 mBackupHandler.sendMessage(msg); 8819 } 8820 } 8821 8822 // ----- Restore session ----- 8823 8824 class ActiveRestoreSession extends IRestoreSession.Stub { 8825 private static final String TAG = "RestoreSession"; 8826 8827 private String mPackageName; 8828 private IBackupTransport mRestoreTransport = null; 8829 RestoreSet[] mRestoreSets = null; 8830 boolean mEnded = false; 8831 boolean mTimedOut = false; 8832 8833 ActiveRestoreSession(String packageName, String transport) { 8834 mPackageName = packageName; 8835 mRestoreTransport = getTransport(transport); 8836 } 8837 8838 public void markTimedOut() { 8839 mTimedOut = true; 8840 } 8841 8842 // --- Binder interface --- 8843 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) { 8844 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8845 "getAvailableRestoreSets"); 8846 if (observer == null) { 8847 throw new IllegalArgumentException("Observer must not be null"); 8848 } 8849 8850 if (mEnded) { 8851 throw new IllegalStateException("Restore session already ended"); 8852 } 8853 8854 if (mTimedOut) { 8855 Slog.i(TAG, "Session already timed out"); 8856 return -1; 8857 } 8858 8859 long oldId = Binder.clearCallingIdentity(); 8860 try { 8861 if (mRestoreTransport == null) { 8862 Slog.w(TAG, "Null transport getting restore sets"); 8863 return -1; 8864 } 8865 8866 // We know we're doing legit work now, so halt the timeout 8867 // until we're done. It gets started again when the result 8868 // comes in. 8869 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 8870 8871 // spin off the transport request to our service thread 8872 mWakelock.acquire(); 8873 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS, 8874 new RestoreGetSetsParams(mRestoreTransport, this, observer)); 8875 mBackupHandler.sendMessage(msg); 8876 return 0; 8877 } catch (Exception e) { 8878 Slog.e(TAG, "Error in getAvailableRestoreSets", e); 8879 return -1; 8880 } finally { 8881 Binder.restoreCallingIdentity(oldId); 8882 } 8883 } 8884 8885 public synchronized int restoreAll(long token, IRestoreObserver observer) { 8886 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8887 "performRestore"); 8888 8889 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token) 8890 + " observer=" + observer); 8891 8892 if (mEnded) { 8893 throw new IllegalStateException("Restore session already ended"); 8894 } 8895 8896 if (mTimedOut) { 8897 Slog.i(TAG, "Session already timed out"); 8898 return -1; 8899 } 8900 8901 if (mRestoreTransport == null || mRestoreSets == null) { 8902 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 8903 return -1; 8904 } 8905 8906 if (mPackageName != null) { 8907 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 8908 return -1; 8909 } 8910 8911 String dirName; 8912 try { 8913 dirName = mRestoreTransport.transportDirName(); 8914 } catch (RemoteException e) { 8915 // Transport went AWOL; fail. 8916 Slog.e(TAG, "Unable to contact transport for restore"); 8917 return -1; 8918 } 8919 8920 synchronized (mQueueLock) { 8921 for (int i = 0; i < mRestoreSets.length; i++) { 8922 if (token == mRestoreSets[i].token) { 8923 // Real work, so stop the session timeout until we finalize the restore 8924 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 8925 8926 long oldId = Binder.clearCallingIdentity(); 8927 mWakelock.acquire(); 8928 if (MORE_DEBUG) { 8929 Slog.d(TAG, "restoreAll() kicking off"); 8930 } 8931 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 8932 msg.obj = new RestoreParams(mRestoreTransport, dirName, 8933 observer, token); 8934 mBackupHandler.sendMessage(msg); 8935 Binder.restoreCallingIdentity(oldId); 8936 return 0; 8937 } 8938 } 8939 } 8940 8941 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 8942 return -1; 8943 } 8944 8945 // Restores of more than a single package are treated as 'system' restores 8946 public synchronized int restoreSome(long token, IRestoreObserver observer, 8947 String[] packages) { 8948 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8949 "performRestore"); 8950 8951 if (DEBUG) { 8952 StringBuilder b = new StringBuilder(128); 8953 b.append("restoreSome token="); 8954 b.append(Long.toHexString(token)); 8955 b.append(" observer="); 8956 b.append(observer.toString()); 8957 b.append(" packages="); 8958 if (packages == null) { 8959 b.append("null"); 8960 } else { 8961 b.append('{'); 8962 boolean first = true; 8963 for (String s : packages) { 8964 if (!first) { 8965 b.append(", "); 8966 } else first = false; 8967 b.append(s); 8968 } 8969 b.append('}'); 8970 } 8971 Slog.d(TAG, b.toString()); 8972 } 8973 8974 if (mEnded) { 8975 throw new IllegalStateException("Restore session already ended"); 8976 } 8977 8978 if (mTimedOut) { 8979 Slog.i(TAG, "Session already timed out"); 8980 return -1; 8981 } 8982 8983 if (mRestoreTransport == null || mRestoreSets == null) { 8984 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 8985 return -1; 8986 } 8987 8988 if (mPackageName != null) { 8989 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 8990 return -1; 8991 } 8992 8993 String dirName; 8994 try { 8995 dirName = mRestoreTransport.transportDirName(); 8996 } catch (RemoteException e) { 8997 // Transport went AWOL; fail. 8998 Slog.e(TAG, "Unable to contact transport for restore"); 8999 return -1; 9000 } 9001 9002 synchronized (mQueueLock) { 9003 for (int i = 0; i < mRestoreSets.length; i++) { 9004 if (token == mRestoreSets[i].token) { 9005 // Stop the session timeout until we finalize the restore 9006 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9007 9008 long oldId = Binder.clearCallingIdentity(); 9009 mWakelock.acquire(); 9010 if (MORE_DEBUG) { 9011 Slog.d(TAG, "restoreSome() of " + packages.length + " packages"); 9012 } 9013 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9014 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, 9015 packages, packages.length > 1); 9016 mBackupHandler.sendMessage(msg); 9017 Binder.restoreCallingIdentity(oldId); 9018 return 0; 9019 } 9020 } 9021 } 9022 9023 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 9024 return -1; 9025 } 9026 9027 public synchronized int restorePackage(String packageName, IRestoreObserver observer) { 9028 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); 9029 9030 if (mEnded) { 9031 throw new IllegalStateException("Restore session already ended"); 9032 } 9033 9034 if (mTimedOut) { 9035 Slog.i(TAG, "Session already timed out"); 9036 return -1; 9037 } 9038 9039 if (mPackageName != null) { 9040 if (! mPackageName.equals(packageName)) { 9041 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName 9042 + " on session for package " + mPackageName); 9043 return -1; 9044 } 9045 } 9046 9047 PackageInfo app = null; 9048 try { 9049 app = mPackageManager.getPackageInfo(packageName, 0); 9050 } catch (NameNotFoundException nnf) { 9051 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 9052 return -1; 9053 } 9054 9055 // If the caller is not privileged and is not coming from the target 9056 // app's uid, throw a permission exception back to the caller. 9057 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP, 9058 Binder.getCallingPid(), Binder.getCallingUid()); 9059 if ((perm == PackageManager.PERMISSION_DENIED) && 9060 (app.applicationInfo.uid != Binder.getCallingUid())) { 9061 Slog.w(TAG, "restorePackage: bad packageName=" + packageName 9062 + " or calling uid=" + Binder.getCallingUid()); 9063 throw new SecurityException("No permission to restore other packages"); 9064 } 9065 9066 // So far so good; we're allowed to try to restore this package. Now 9067 // check whether there is data for it in the current dataset, falling back 9068 // to the ancestral dataset if not. 9069 long token = getAvailableRestoreToken(packageName); 9070 9071 // If we didn't come up with a place to look -- no ancestral dataset and 9072 // the app has never been backed up from this device -- there's nothing 9073 // to do but return failure. 9074 if (token == 0) { 9075 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring"); 9076 return -1; 9077 } 9078 9079 String dirName; 9080 try { 9081 dirName = mRestoreTransport.transportDirName(); 9082 } catch (RemoteException e) { 9083 // Transport went AWOL; fail. 9084 Slog.e(TAG, "Unable to contact transport for restore"); 9085 return -1; 9086 } 9087 9088 // Stop the session timeout until we finalize the restore 9089 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9090 9091 // Ready to go: enqueue the restore request and claim success 9092 long oldId = Binder.clearCallingIdentity(); 9093 mWakelock.acquire(); 9094 if (MORE_DEBUG) { 9095 Slog.d(TAG, "restorePackage() : " + packageName); 9096 } 9097 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9098 msg.obj = new RestoreParams(mRestoreTransport, dirName, 9099 observer, token, app, 0); 9100 mBackupHandler.sendMessage(msg); 9101 Binder.restoreCallingIdentity(oldId); 9102 return 0; 9103 } 9104 9105 // Posted to the handler to tear down a restore session in a cleanly synchronized way 9106 class EndRestoreRunnable implements Runnable { 9107 BackupManagerService mBackupManager; 9108 ActiveRestoreSession mSession; 9109 9110 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) { 9111 mBackupManager = manager; 9112 mSession = session; 9113 } 9114 9115 public void run() { 9116 // clean up the session's bookkeeping 9117 synchronized (mSession) { 9118 try { 9119 if (mSession.mRestoreTransport != null) { 9120 mSession.mRestoreTransport.finishRestore(); 9121 } 9122 } catch (Exception e) { 9123 Slog.e(TAG, "Error in finishRestore", e); 9124 } finally { 9125 mSession.mRestoreTransport = null; 9126 mSession.mEnded = true; 9127 } 9128 } 9129 9130 // clean up the BackupManagerImpl side of the bookkeeping 9131 // and cancel any pending timeout message 9132 mBackupManager.clearRestoreSession(mSession); 9133 } 9134 } 9135 9136 public synchronized void endRestoreSession() { 9137 if (DEBUG) Slog.d(TAG, "endRestoreSession"); 9138 9139 if (mTimedOut) { 9140 Slog.i(TAG, "Session already timed out"); 9141 return; 9142 } 9143 9144 if (mEnded) { 9145 throw new IllegalStateException("Restore session already ended"); 9146 } 9147 9148 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this)); 9149 } 9150 } 9151 9152 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 9153 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 9154 9155 long identityToken = Binder.clearCallingIdentity(); 9156 try { 9157 if (args != null) { 9158 for (String arg : args) { 9159 if ("-h".equals(arg)) { 9160 pw.println("'dumpsys backup' optional arguments:"); 9161 pw.println(" -h : this help text"); 9162 pw.println(" a[gents] : dump information about defined backup agents"); 9163 return; 9164 } else if ("agents".startsWith(arg)) { 9165 dumpAgents(pw); 9166 return; 9167 } 9168 } 9169 } 9170 dumpInternal(pw); 9171 } finally { 9172 Binder.restoreCallingIdentity(identityToken); 9173 } 9174 } 9175 9176 private void dumpAgents(PrintWriter pw) { 9177 List<PackageInfo> agentPackages = allAgentPackages(); 9178 pw.println("Defined backup agents:"); 9179 for (PackageInfo pkg : agentPackages) { 9180 pw.print(" "); 9181 pw.print(pkg.packageName); pw.println(':'); 9182 pw.print(" "); pw.println(pkg.applicationInfo.backupAgentName); 9183 } 9184 } 9185 9186 private void dumpInternal(PrintWriter pw) { 9187 synchronized (mQueueLock) { 9188 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") 9189 + " / " + (!mProvisioned ? "not " : "") + "provisioned / " 9190 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); 9191 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); 9192 if (mBackupRunning) pw.println("Backup currently running"); 9193 pw.println("Last backup pass started: " + mLastBackupPass 9194 + " (now = " + System.currentTimeMillis() + ')'); 9195 pw.println(" next scheduled: " + mNextBackupPass); 9196 9197 pw.println("Available transports:"); 9198 final String[] transports = listAllTransports(); 9199 if (transports != null) { 9200 for (String t : listAllTransports()) { 9201 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t); 9202 try { 9203 IBackupTransport transport = getTransport(t); 9204 File dir = new File(mBaseStateDir, transport.transportDirName()); 9205 pw.println(" destination: " + transport.currentDestinationString()); 9206 pw.println(" intent: " + transport.configurationIntent()); 9207 for (File f : dir.listFiles()) { 9208 pw.println(" " + f.getName() + " - " + f.length() + " state bytes"); 9209 } 9210 } catch (Exception e) { 9211 Slog.e(TAG, "Error in transport", e); 9212 pw.println(" Error: " + e); 9213 } 9214 } 9215 } 9216 9217 pw.println("Pending init: " + mPendingInits.size()); 9218 for (String s : mPendingInits) { 9219 pw.println(" " + s); 9220 } 9221 9222 if (DEBUG_BACKUP_TRACE) { 9223 synchronized (mBackupTrace) { 9224 if (!mBackupTrace.isEmpty()) { 9225 pw.println("Most recent backup trace:"); 9226 for (String s : mBackupTrace) { 9227 pw.println(" " + s); 9228 } 9229 } 9230 } 9231 } 9232 9233 int N = mBackupParticipants.size(); 9234 pw.println("Participants:"); 9235 for (int i=0; i<N; i++) { 9236 int uid = mBackupParticipants.keyAt(i); 9237 pw.print(" uid: "); 9238 pw.println(uid); 9239 HashSet<String> participants = mBackupParticipants.valueAt(i); 9240 for (String app: participants) { 9241 pw.println(" " + app); 9242 } 9243 } 9244 9245 pw.println("Ancestral packages: " 9246 + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); 9247 if (mAncestralPackages != null) { 9248 for (String pkg : mAncestralPackages) { 9249 pw.println(" " + pkg); 9250 } 9251 } 9252 9253 pw.println("Ever backed up: " + mEverStoredApps.size()); 9254 for (String pkg : mEverStoredApps) { 9255 pw.println(" " + pkg); 9256 } 9257 9258 pw.println("Pending key/value backup: " + mPendingBackups.size()); 9259 for (BackupRequest req : mPendingBackups.values()) { 9260 pw.println(" " + req); 9261 } 9262 9263 pw.println("Full backup queue:" + mFullBackupQueue.size()); 9264 for (FullBackupEntry entry : mFullBackupQueue) { 9265 pw.print(" "); pw.print(entry.lastBackup); 9266 pw.print(" : "); pw.println(entry.packageName); 9267 } 9268 } 9269 } 9270} 9271