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