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