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