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