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