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