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