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