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