BackupManagerService.java revision ad869cc908ff22956f8df47cb127e3446793b787
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 if (currentPackage.applicationInfo != null) { 4666 Slog.i(TAG, "Unbinding agent in " + packageName); 4667 addBackupTrace("unbinding " + packageName); 4668 try { 4669 mActivityManager.unbindBackupAgent(currentPackage.applicationInfo); 4670 } catch (RemoteException e) { /* can't happen; activity manager is local */ } 4671 } 4672 } 4673 } catch (Exception e) { 4674 backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED; 4675 Slog.w(TAG, "Exception trying full transport backup", e); 4676 } finally { 4677 if (DEBUG) { 4678 Slog.i(TAG, "Full backup completed with status: " + backupRunStatus); 4679 } 4680 sendBackupFinished(mBackupObserver, backupRunStatus); 4681 4682 cleanUpPipes(transportPipes); 4683 cleanUpPipes(enginePipes); 4684 4685 if (mJob != null) { 4686 mJob.finishBackupPass(); 4687 } 4688 4689 synchronized (mQueueLock) { 4690 mRunningFullBackupTask = null; 4691 } 4692 4693 mLatch.countDown(); 4694 4695 // Now that we're actually done with schedule-driven work, reschedule 4696 // the next pass based on the new queue state. 4697 if (mUpdateSchedule) { 4698 scheduleNextFullBackupJob(backoff); 4699 } 4700 Slog.i(BackupManagerService.TAG, "Full data backup pass finished."); 4701 mWakelock.release(); 4702 } 4703 } 4704 4705 void cleanUpPipes(ParcelFileDescriptor[] pipes) { 4706 if (pipes != null) { 4707 if (pipes[0] != null) { 4708 ParcelFileDescriptor fd = pipes[0]; 4709 pipes[0] = null; 4710 try { 4711 fd.close(); 4712 } catch (IOException e) { 4713 Slog.w(TAG, "Unable to close pipe!"); 4714 } 4715 } 4716 if (pipes[1] != null) { 4717 ParcelFileDescriptor fd = pipes[1]; 4718 pipes[1] = null; 4719 try { 4720 fd.close(); 4721 } catch (IOException e) { 4722 Slog.w(TAG, "Unable to close pipe!"); 4723 } 4724 } 4725 } 4726 } 4727 4728 // Run the backup and pipe it back to the given socket -- expects to run on 4729 // a standalone thread. The runner owns this half of the pipe, and closes 4730 // it to indicate EOD to the other end. 4731 class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight { 4732 final AtomicLong mResult = new AtomicLong(BackupTransport.AGENT_ERROR); 4733 final CountDownLatch mLatch = new CountDownLatch(1); 4734 final IBackupTransport mTransport; 4735 4736 public SinglePackageBackupPreflight(IBackupTransport transport) { 4737 mTransport = transport; 4738 } 4739 4740 @Override 4741 public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) { 4742 int result; 4743 try { 4744 final int token = generateToken(); 4745 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this); 4746 addBackupTrace("preflighting"); 4747 if (MORE_DEBUG) { 4748 Slog.d(TAG, "Preflighting full payload of " + pkg.packageName); 4749 } 4750 agent.doMeasureFullBackup(token, mBackupManagerBinder); 4751 4752 // Now wait to get our result back. If this backstop timeout is reached without 4753 // the latch being thrown, flow will continue as though a result or "normal" 4754 // timeout had been produced. In case of a real backstop timeout, mResult 4755 // will still contain the value it was constructed with, AGENT_ERROR, which 4756 // intentionaly falls into the "just report failure" code. 4757 mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 4758 4759 long totalSize = mResult.get(); 4760 // If preflight timed out, mResult will contain error code as int. 4761 if (totalSize < 0) { 4762 return (int) totalSize; 4763 } 4764 if (MORE_DEBUG) { 4765 Slog.v(TAG, "Got preflight response; size=" + totalSize); 4766 } 4767 4768 result = mTransport.checkFullBackupSize(totalSize); 4769 if (result == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { 4770 final long quota = mTransport.getBackupQuota(pkg.packageName, true); 4771 if (MORE_DEBUG) { 4772 Slog.d(TAG, "Package hit quota limit on preflight " + 4773 pkg.packageName + ": " + totalSize + " of " + quota); 4774 } 4775 agent.doQuotaExceeded(totalSize, quota); 4776 } 4777 } catch (Exception e) { 4778 Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage()); 4779 result = BackupTransport.AGENT_ERROR; 4780 } 4781 return result; 4782 } 4783 4784 @Override 4785 public void execute() { 4786 // Unused in this case 4787 } 4788 4789 @Override 4790 public void operationComplete(long result) { 4791 // got the callback, and our preflightFullBackup() method is waiting for the result 4792 if (MORE_DEBUG) { 4793 Slog.i(TAG, "Preflight op complete, result=" + result); 4794 } 4795 mResult.set(result); 4796 mLatch.countDown(); 4797 } 4798 4799 @Override 4800 public void handleTimeout() { 4801 if (MORE_DEBUG) { 4802 Slog.i(TAG, "Preflight timeout; failing"); 4803 } 4804 mResult.set(BackupTransport.AGENT_ERROR); 4805 mLatch.countDown(); 4806 } 4807 4808 @Override 4809 public long getExpectedSizeOrErrorCode() { 4810 try { 4811 mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 4812 return mResult.get(); 4813 } catch (InterruptedException e) { 4814 return BackupTransport.NO_MORE_DATA; 4815 } 4816 } 4817 } 4818 4819 class SinglePackageBackupRunner implements Runnable, BackupRestoreTask { 4820 final ParcelFileDescriptor mOutput; 4821 final PackageInfo mTarget; 4822 final FullBackupPreflight mPreflight; 4823 final CountDownLatch mPreflightLatch; 4824 final CountDownLatch mBackupLatch; 4825 private FullBackupEngine mEngine; 4826 private volatile int mPreflightResult; 4827 private volatile int mBackupResult; 4828 4829 SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target, 4830 IBackupTransport transport) throws IOException { 4831 mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor()); 4832 mTarget = target; 4833 mPreflight = new SinglePackageBackupPreflight(transport); 4834 mPreflightLatch = new CountDownLatch(1); 4835 mBackupLatch = new CountDownLatch(1); 4836 mPreflightResult = BackupTransport.AGENT_ERROR; 4837 mBackupResult = BackupTransport.AGENT_ERROR; 4838 } 4839 4840 @Override 4841 public void run() { 4842 FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor()); 4843 mEngine = new FullBackupEngine(out, mPreflight, mTarget, false, this); 4844 try { 4845 try { 4846 mPreflightResult = mEngine.preflightCheck(); 4847 } finally { 4848 mPreflightLatch.countDown(); 4849 } 4850 // If there is no error on preflight, continue backup. 4851 if (mPreflightResult == BackupTransport.TRANSPORT_OK) { 4852 mBackupResult = mEngine.backupOnePackage(); 4853 } 4854 } catch (Exception e) { 4855 Slog.e(TAG, "Exception during full package backup of " + mTarget.packageName); 4856 } finally { 4857 mBackupLatch.countDown(); 4858 try { 4859 mOutput.close(); 4860 } catch (IOException e) { 4861 Slog.w(TAG, "Error closing transport pipe in runner"); 4862 } 4863 } 4864 } 4865 4866 public void sendQuotaExceeded(final long backupDataBytes, final long quotaBytes) { 4867 mEngine.sendQuotaExceeded(backupDataBytes, quotaBytes); 4868 } 4869 4870 // If preflight succeeded, returns positive number - preflight size, 4871 // otherwise return negative error code. 4872 long getPreflightResultBlocking() { 4873 try { 4874 mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 4875 if (mPreflightResult == BackupTransport.TRANSPORT_OK) { 4876 return mPreflight.getExpectedSizeOrErrorCode(); 4877 } else { 4878 return mPreflightResult; 4879 } 4880 } catch (InterruptedException e) { 4881 return BackupTransport.AGENT_ERROR; 4882 } 4883 } 4884 4885 int getBackupResultBlocking() { 4886 try { 4887 mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 4888 return mBackupResult; 4889 } catch (InterruptedException e) { 4890 return BackupTransport.AGENT_ERROR; 4891 } 4892 } 4893 4894 4895 // BackupRestoreTask interface: specifically, timeout detection 4896 4897 @Override 4898 public void execute() { /* intentionally empty */ } 4899 4900 @Override 4901 public void operationComplete(long result) { /* intentionally empty */ } 4902 4903 @Override 4904 public void handleTimeout() { 4905 if (DEBUG) { 4906 Slog.w(TAG, "Full backup timeout of " + mTarget.packageName); 4907 } 4908 tearDownAgentAndKill(mTarget.applicationInfo); 4909 } 4910 } 4911 } 4912 4913 // ----- Full-data backup scheduling ----- 4914 4915 /** 4916 * Schedule a job to tell us when it's a good time to run a full backup 4917 */ 4918 void scheduleNextFullBackupJob(long transportMinLatency) { 4919 synchronized (mQueueLock) { 4920 if (mFullBackupQueue.size() > 0) { 4921 // schedule the next job at the point in the future when the least-recently 4922 // backed up app comes due for backup again; or immediately if it's already 4923 // due. 4924 final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup; 4925 final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup; 4926 final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL) 4927 ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0; 4928 final long latency = Math.max(transportMinLatency, appLatency); 4929 Runnable r = new Runnable() { 4930 @Override public void run() { 4931 FullBackupJob.schedule(mContext, latency); 4932 } 4933 }; 4934 mBackupHandler.postDelayed(r, 2500); 4935 } else { 4936 if (DEBUG_SCHEDULING) { 4937 Slog.i(TAG, "Full backup queue empty; not scheduling"); 4938 } 4939 } 4940 } 4941 } 4942 4943 /** 4944 * Remove a package from the full-data queue. 4945 */ 4946 void dequeueFullBackupLocked(String packageName) { 4947 final int N = mFullBackupQueue.size(); 4948 for (int i = N-1; i >= 0; i--) { 4949 final FullBackupEntry e = mFullBackupQueue.get(i); 4950 if (packageName.equals(e.packageName)) { 4951 mFullBackupQueue.remove(i); 4952 } 4953 } 4954 } 4955 4956 /** 4957 * Enqueue full backup for the given app, with a note about when it last ran. 4958 */ 4959 void enqueueFullBackup(String packageName, long lastBackedUp) { 4960 FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp); 4961 synchronized (mQueueLock) { 4962 // First, sanity check that we aren't adding a duplicate. Slow but 4963 // straightforward; we'll have at most on the order of a few hundred 4964 // items in this list. 4965 dequeueFullBackupLocked(packageName); 4966 4967 // This is also slow but easy for modest numbers of apps: work backwards 4968 // from the end of the queue until we find an item whose last backup 4969 // time was before this one, then insert this new entry after it. If we're 4970 // adding something new we don't bother scanning, and just prepend. 4971 int which = -1; 4972 if (lastBackedUp > 0) { 4973 for (which = mFullBackupQueue.size() - 1; which >= 0; which--) { 4974 final FullBackupEntry entry = mFullBackupQueue.get(which); 4975 if (entry.lastBackup <= lastBackedUp) { 4976 mFullBackupQueue.add(which + 1, newEntry); 4977 break; 4978 } 4979 } 4980 } 4981 if (which < 0) { 4982 // this one is earlier than any existing one, so prepend 4983 mFullBackupQueue.add(0, newEntry); 4984 } 4985 } 4986 writeFullBackupScheduleAsync(); 4987 } 4988 4989 private boolean fullBackupAllowable(IBackupTransport transport) { 4990 if (transport == null) { 4991 Slog.w(TAG, "Transport not present; full data backup not performed"); 4992 return false; 4993 } 4994 4995 // Don't proceed unless we have already established package metadata 4996 // for the current dataset via a key/value backup pass. 4997 try { 4998 File stateDir = new File(mBaseStateDir, transport.transportDirName()); 4999 File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL); 5000 if (pmState.length() <= 0) { 5001 if (DEBUG) { 5002 Slog.i(TAG, "Full backup requested but dataset not yet initialized"); 5003 } 5004 return false; 5005 } 5006 } catch (Exception e) { 5007 Slog.w(TAG, "Unable to contact transport"); 5008 return false; 5009 } 5010 5011 return true; 5012 } 5013 5014 /** 5015 * Conditions are right for a full backup operation, so run one. The model we use is 5016 * to perform one app backup per scheduled job execution, and to reschedule the job 5017 * with zero latency as long as conditions remain right and we still have work to do. 5018 * 5019 * <p>This is the "start a full backup operation" entry point called by the scheduled job. 5020 * 5021 * @return Whether ongoing work will continue. The return value here will be passed 5022 * along as the return value to the scheduled job's onStartJob() callback. 5023 */ 5024 boolean beginFullBackup(FullBackupJob scheduledJob) { 5025 long now = System.currentTimeMillis(); 5026 FullBackupEntry entry = null; 5027 long latency = MIN_FULL_BACKUP_INTERVAL; 5028 5029 if (!mEnabled || !mProvisioned) { 5030 // Backups are globally disabled, so don't proceed. We also don't reschedule 5031 // the job driving automatic backups; that job will be scheduled again when 5032 // the user enables backup. 5033 if (MORE_DEBUG) { 5034 Slog.i(TAG, "beginFullBackup but e=" + mEnabled 5035 + " p=" + mProvisioned + "; ignoring"); 5036 } 5037 return false; 5038 } 5039 5040 // Don't run the backup if we're in battery saver mode, but reschedule 5041 // to try again in the not-so-distant future. 5042 if (mPowerManager.isPowerSaveMode()) { 5043 if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode"); 5044 FullBackupJob.schedule(mContext, KeyValueBackupJob.BATCH_INTERVAL); 5045 return false; 5046 } 5047 5048 if (DEBUG_SCHEDULING) { 5049 Slog.i(TAG, "Beginning scheduled full backup operation"); 5050 } 5051 5052 // Great; we're able to run full backup jobs now. See if we have any work to do. 5053 synchronized (mQueueLock) { 5054 if (mRunningFullBackupTask != null) { 5055 Slog.e(TAG, "Backup triggered but one already/still running!"); 5056 return false; 5057 } 5058 5059 // At this point we think that we have work to do, but possibly not right now. 5060 // Any exit without actually running backups will also require that we 5061 // reschedule the job. 5062 boolean runBackup = true; 5063 boolean headBusy; 5064 5065 do { 5066 // Recheck each time, because culling due to ineligibility may 5067 // have emptied the queue. 5068 if (mFullBackupQueue.size() == 0) { 5069 // no work to do so just bow out 5070 if (DEBUG) { 5071 Slog.i(TAG, "Backup queue empty; doing nothing"); 5072 } 5073 runBackup = false; 5074 break; 5075 } 5076 5077 headBusy = false; 5078 5079 if (!fullBackupAllowable(getTransport(mCurrentTransport))) { 5080 if (MORE_DEBUG) { 5081 Slog.i(TAG, "Preconditions not met; not running full backup"); 5082 } 5083 runBackup = false; 5084 // Typically this means we haven't run a key/value backup yet. Back off 5085 // full-backup operations by the key/value job's run interval so that 5086 // next time we run, we are likely to be able to make progress. 5087 latency = KeyValueBackupJob.BATCH_INTERVAL; 5088 } 5089 5090 if (runBackup) { 5091 entry = mFullBackupQueue.get(0); 5092 long timeSinceRun = now - entry.lastBackup; 5093 runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL); 5094 if (!runBackup) { 5095 // It's too early to back up the next thing in the queue, so bow out 5096 if (MORE_DEBUG) { 5097 Slog.i(TAG, "Device ready but too early to back up next app"); 5098 } 5099 // Wait until the next app in the queue falls due for a full data backup 5100 latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun; 5101 break; // we know we aren't doing work yet, so bail. 5102 } 5103 5104 try { 5105 PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0); 5106 if (!appGetsFullBackup(appInfo)) { 5107 // The head app isn't supposed to get full-data backups [any more]; 5108 // so we cull it and force a loop around to consider the new head 5109 // app. 5110 if (MORE_DEBUG) { 5111 Slog.i(TAG, "Culling package " + entry.packageName 5112 + " in full-backup queue but not eligible"); 5113 } 5114 mFullBackupQueue.remove(0); 5115 headBusy = true; // force the while() condition 5116 continue; 5117 } 5118 5119 final int privFlags = appInfo.applicationInfo.privateFlags; 5120 headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0 5121 && mActivityManager.isAppForeground(appInfo.applicationInfo.uid); 5122 5123 if (headBusy) { 5124 final long nextEligible = System.currentTimeMillis() 5125 + BUSY_BACKOFF_MIN_MILLIS 5126 + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ); 5127 if (DEBUG_SCHEDULING) { 5128 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 5129 Slog.i(TAG, "Full backup time but " + entry.packageName 5130 + " is busy; deferring to " 5131 + sdf.format(new Date(nextEligible))); 5132 } 5133 // This relocates the app's entry from the head of the queue to 5134 // its order-appropriate position further down, so upon looping 5135 // a new candidate will be considered at the head. 5136 enqueueFullBackup(entry.packageName, 5137 nextEligible - MIN_FULL_BACKUP_INTERVAL); 5138 } 5139 } catch (NameNotFoundException nnf) { 5140 // So, we think we want to back this up, but it turns out the package 5141 // in question is no longer installed. We want to drop it from the 5142 // queue entirely and move on, but if there's nothing else in the queue 5143 // we should bail entirely. headBusy cannot have been set to true yet. 5144 runBackup = (mFullBackupQueue.size() > 1); 5145 } catch (RemoteException e) { 5146 // Cannot happen; the Activity Manager is in the same process 5147 } 5148 } 5149 } while (headBusy); 5150 5151 if (!runBackup) { 5152 if (DEBUG_SCHEDULING) { 5153 Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency); 5154 } 5155 final long deferTime = latency; // pin for the closure 5156 mBackupHandler.post(new Runnable() { 5157 @Override public void run() { 5158 FullBackupJob.schedule(mContext, deferTime); 5159 } 5160 }); 5161 return false; 5162 } 5163 5164 // Okay, the top thing is ready for backup now. Do it. 5165 mFullBackupQueue.remove(0); 5166 CountDownLatch latch = new CountDownLatch(1); 5167 String[] pkg = new String[] {entry.packageName}; 5168 mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true, 5169 scheduledJob, latch, null, false /* userInitiated */); 5170 // Acquiring wakelock for PerformFullTransportBackupTask before its start. 5171 mWakelock.acquire(); 5172 (new Thread(mRunningFullBackupTask)).start(); 5173 } 5174 5175 return true; 5176 } 5177 5178 // The job scheduler says our constraints don't hold any more, 5179 // so tear down any ongoing backup task right away. 5180 void endFullBackup() { 5181 synchronized (mQueueLock) { 5182 if (mRunningFullBackupTask != null) { 5183 if (DEBUG_SCHEDULING) { 5184 Slog.i(TAG, "Telling running backup to stop"); 5185 } 5186 mRunningFullBackupTask.setRunning(false); 5187 } 5188 } 5189 } 5190 5191 // ----- Restore infrastructure ----- 5192 5193 abstract class RestoreEngine { 5194 static final String TAG = "RestoreEngine"; 5195 5196 public static final int SUCCESS = 0; 5197 public static final int TARGET_FAILURE = -2; 5198 public static final int TRANSPORT_FAILURE = -3; 5199 5200 private AtomicBoolean mRunning = new AtomicBoolean(false); 5201 private AtomicInteger mResult = new AtomicInteger(SUCCESS); 5202 5203 public boolean isRunning() { 5204 return mRunning.get(); 5205 } 5206 5207 public void setRunning(boolean stillRunning) { 5208 synchronized (mRunning) { 5209 mRunning.set(stillRunning); 5210 mRunning.notifyAll(); 5211 } 5212 } 5213 5214 public int waitForResult() { 5215 synchronized (mRunning) { 5216 while (isRunning()) { 5217 try { 5218 mRunning.wait(); 5219 } catch (InterruptedException e) {} 5220 } 5221 } 5222 return getResult(); 5223 } 5224 5225 public int getResult() { 5226 return mResult.get(); 5227 } 5228 5229 public void setResult(int result) { 5230 mResult.set(result); 5231 } 5232 5233 // TODO: abstract restore state and APIs 5234 } 5235 5236 // ----- Full restore from a file/socket ----- 5237 5238 // Description of a file in the restore datastream 5239 static class FileMetadata { 5240 String packageName; // name of the owning app 5241 String installerPackageName; // name of the market-type app that installed the owner 5242 int type; // e.g. BackupAgent.TYPE_DIRECTORY 5243 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN 5244 String path; // subpath within the semantic domain 5245 long mode; // e.g. 0666 (actually int) 5246 long mtime; // last mod time, UTC time_t (actually int) 5247 long size; // bytes of content 5248 5249 @Override 5250 public String toString() { 5251 StringBuilder sb = new StringBuilder(128); 5252 sb.append("FileMetadata{"); 5253 sb.append(packageName); sb.append(','); 5254 sb.append(type); sb.append(','); 5255 sb.append(domain); sb.append(':'); sb.append(path); sb.append(','); 5256 sb.append(size); 5257 sb.append('}'); 5258 return sb.toString(); 5259 } 5260 } 5261 5262 enum RestorePolicy { 5263 IGNORE, 5264 ACCEPT, 5265 ACCEPT_IF_APK 5266 } 5267 5268 // Full restore engine, used by both adb restore and transport-based full restore 5269 class FullRestoreEngine extends RestoreEngine { 5270 // Task in charge of monitoring timeouts 5271 BackupRestoreTask mMonitorTask; 5272 5273 // Dedicated observer, if any 5274 IFullBackupRestoreObserver mObserver; 5275 5276 // Where we're delivering the file data as we go 5277 IBackupAgent mAgent; 5278 5279 // Are we permitted to only deliver a specific package's metadata? 5280 PackageInfo mOnlyPackage; 5281 5282 boolean mAllowApks; 5283 boolean mAllowObbs; 5284 5285 // Which package are we currently handling data for? 5286 String mAgentPackage; 5287 5288 // Info for working with the target app process 5289 ApplicationInfo mTargetApp; 5290 5291 // Machinery for restoring OBBs 5292 FullBackupObbConnection mObbConnection = null; 5293 5294 // possible handling states for a given package in the restore dataset 5295 final HashMap<String, RestorePolicy> mPackagePolicies 5296 = new HashMap<String, RestorePolicy>(); 5297 5298 // installer package names for each encountered app, derived from the manifests 5299 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 5300 5301 // Signatures for a given package found in its manifest file 5302 final HashMap<String, Signature[]> mManifestSignatures 5303 = new HashMap<String, Signature[]>(); 5304 5305 // Packages we've already wiped data on when restoring their first file 5306 final HashSet<String> mClearedPackages = new HashSet<String>(); 5307 5308 // How much data have we moved? 5309 long mBytes; 5310 5311 // Working buffer 5312 byte[] mBuffer; 5313 5314 // Pipes for moving data 5315 ParcelFileDescriptor[] mPipes = null; 5316 5317 // Widget blob to be restored out-of-band 5318 byte[] mWidgetData = null; 5319 5320 // Runner that can be placed in a separate thread to do in-process 5321 // invocations of the full restore API asynchronously. Used by adb restore. 5322 class RestoreFileRunnable implements Runnable { 5323 IBackupAgent mAgent; 5324 FileMetadata mInfo; 5325 ParcelFileDescriptor mSocket; 5326 int mToken; 5327 5328 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 5329 ParcelFileDescriptor socket, int token) throws IOException { 5330 mAgent = agent; 5331 mInfo = info; 5332 mToken = token; 5333 5334 // This class is used strictly for process-local binder invocations. The 5335 // semantics of ParcelFileDescriptor differ in this case; in particular, we 5336 // do not automatically get a 'dup'ed descriptor that we can can continue 5337 // to use asynchronously from the caller. So, we make sure to dup it ourselves 5338 // before proceeding to do the restore. 5339 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 5340 } 5341 5342 @Override 5343 public void run() { 5344 try { 5345 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 5346 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 5347 mToken, mBackupManagerBinder); 5348 } catch (RemoteException e) { 5349 // never happens; this is used strictly for local binder calls 5350 } 5351 } 5352 } 5353 5354 public FullRestoreEngine(BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer, 5355 PackageInfo onlyPackage, boolean allowApks, boolean allowObbs) { 5356 mMonitorTask = monitorTask; 5357 mObserver = observer; 5358 mOnlyPackage = onlyPackage; 5359 mAllowApks = allowApks; 5360 mAllowObbs = allowObbs; 5361 mBuffer = new byte[32 * 1024]; 5362 mBytes = 0; 5363 } 5364 5365 public IBackupAgent getAgent() { 5366 return mAgent; 5367 } 5368 5369 public byte[] getWidgetData() { 5370 return mWidgetData; 5371 } 5372 5373 public boolean restoreOneFile(InputStream instream, boolean mustKillAgent) { 5374 if (!isRunning()) { 5375 Slog.w(TAG, "Restore engine used after halting"); 5376 return false; 5377 } 5378 5379 FileMetadata info; 5380 try { 5381 if (MORE_DEBUG) { 5382 Slog.v(TAG, "Reading tar header for restoring file"); 5383 } 5384 info = readTarHeaders(instream); 5385 if (info != null) { 5386 if (MORE_DEBUG) { 5387 dumpFileMetadata(info); 5388 } 5389 5390 final String pkg = info.packageName; 5391 if (!pkg.equals(mAgentPackage)) { 5392 // In the single-package case, it's a semantic error to expect 5393 // one app's data but see a different app's on the wire 5394 if (mOnlyPackage != null) { 5395 if (!pkg.equals(mOnlyPackage.packageName)) { 5396 Slog.w(TAG, "Expected data for " + mOnlyPackage 5397 + " but saw " + pkg); 5398 setResult(RestoreEngine.TRANSPORT_FAILURE); 5399 setRunning(false); 5400 return false; 5401 } 5402 } 5403 5404 // okay, change in package; set up our various 5405 // bookkeeping if we haven't seen it yet 5406 if (!mPackagePolicies.containsKey(pkg)) { 5407 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5408 } 5409 5410 // Clean up the previous agent relationship if necessary, 5411 // and let the observer know we're considering a new app. 5412 if (mAgent != null) { 5413 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 5414 // Now we're really done 5415 tearDownPipes(); 5416 tearDownAgent(mTargetApp); 5417 mTargetApp = null; 5418 mAgentPackage = null; 5419 } 5420 } 5421 5422 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 5423 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 5424 mPackageInstallers.put(pkg, info.installerPackageName); 5425 // We've read only the manifest content itself at this point, 5426 // so consume the footer before looping around to the next 5427 // input file 5428 skipTarPadding(info.size, instream); 5429 sendOnRestorePackage(pkg); 5430 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 5431 // Metadata blobs! 5432 readMetadata(info, instream); 5433 skipTarPadding(info.size, instream); 5434 } else { 5435 // Non-manifest, so it's actual file data. Is this a package 5436 // we're ignoring? 5437 boolean okay = true; 5438 RestorePolicy policy = mPackagePolicies.get(pkg); 5439 switch (policy) { 5440 case IGNORE: 5441 okay = false; 5442 break; 5443 5444 case ACCEPT_IF_APK: 5445 // If we're in accept-if-apk state, then the first file we 5446 // see MUST be the apk. 5447 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5448 if (DEBUG) Slog.d(TAG, "APK file; installing"); 5449 // Try to install the app. 5450 String installerName = mPackageInstallers.get(pkg); 5451 okay = installApk(info, installerName, instream); 5452 // good to go; promote to ACCEPT 5453 mPackagePolicies.put(pkg, (okay) 5454 ? RestorePolicy.ACCEPT 5455 : RestorePolicy.IGNORE); 5456 // At this point we've consumed this file entry 5457 // ourselves, so just strip the tar footer and 5458 // go on to the next file in the input stream 5459 skipTarPadding(info.size, instream); 5460 return true; 5461 } else { 5462 // File data before (or without) the apk. We can't 5463 // handle it coherently in this case so ignore it. 5464 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5465 okay = false; 5466 } 5467 break; 5468 5469 case ACCEPT: 5470 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5471 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 5472 // we can take the data without the apk, so we 5473 // *want* to do so. skip the apk by declaring this 5474 // one file not-okay without changing the restore 5475 // policy for the package. 5476 okay = false; 5477 } 5478 break; 5479 5480 default: 5481 // Something has gone dreadfully wrong when determining 5482 // the restore policy from the manifest. Ignore the 5483 // rest of this package's data. 5484 Slog.e(TAG, "Invalid policy from manifest"); 5485 okay = false; 5486 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5487 break; 5488 } 5489 5490 // Is it a *file* we need to drop? 5491 if (!isRestorableFile(info)) { 5492 okay = false; 5493 } 5494 5495 // If the policy is satisfied, go ahead and set up to pipe the 5496 // data to the agent. 5497 if (DEBUG && okay && mAgent != null) { 5498 Slog.i(TAG, "Reusing existing agent instance"); 5499 } 5500 if (okay && mAgent == null) { 5501 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 5502 5503 try { 5504 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 5505 5506 // If we haven't sent any data to this app yet, we probably 5507 // need to clear it first. Check that. 5508 if (!mClearedPackages.contains(pkg)) { 5509 // apps with their own backup agents are 5510 // responsible for coherently managing a full 5511 // restore. 5512 if (mTargetApp.backupAgentName == null) { 5513 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 5514 clearApplicationDataSynchronous(pkg); 5515 } else { 5516 if (MORE_DEBUG) Slog.d(TAG, "backup agent (" 5517 + mTargetApp.backupAgentName + ") => no clear"); 5518 } 5519 mClearedPackages.add(pkg); 5520 } else { 5521 if (MORE_DEBUG) { 5522 Slog.d(TAG, "We've initialized this app already; no clear required"); 5523 } 5524 } 5525 5526 // All set; now set up the IPC and launch the agent 5527 setUpPipes(); 5528 mAgent = bindToAgentSynchronous(mTargetApp, 5529 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 5530 mAgentPackage = pkg; 5531 } catch (IOException e) { 5532 // fall through to error handling 5533 } catch (NameNotFoundException e) { 5534 // fall through to error handling 5535 } 5536 5537 if (mAgent == null) { 5538 Slog.e(TAG, "Unable to create agent for " + pkg); 5539 okay = false; 5540 tearDownPipes(); 5541 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5542 } 5543 } 5544 5545 // Sanity check: make sure we never give data to the wrong app. This 5546 // should never happen but a little paranoia here won't go amiss. 5547 if (okay && !pkg.equals(mAgentPackage)) { 5548 Slog.e(TAG, "Restoring data for " + pkg 5549 + " but agent is for " + mAgentPackage); 5550 okay = false; 5551 } 5552 5553 // At this point we have an agent ready to handle the full 5554 // restore data as well as a pipe for sending data to 5555 // that agent. Tell the agent to start reading from the 5556 // pipe. 5557 if (okay) { 5558 boolean agentSuccess = true; 5559 long toCopy = info.size; 5560 final int token = generateToken(); 5561 try { 5562 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, 5563 mMonitorTask); 5564 5565 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 5566 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 5567 + " : " + info.path); 5568 mObbConnection.restoreObbFile(pkg, mPipes[0], 5569 info.size, info.type, info.path, info.mode, 5570 info.mtime, token, mBackupManagerBinder); 5571 } else { 5572 if (MORE_DEBUG) Slog.d(TAG, "Invoking agent to restore file " 5573 + info.path); 5574 // fire up the app's agent listening on the socket. If 5575 // the agent is running in the system process we can't 5576 // just invoke it asynchronously, so we provide a thread 5577 // for it here. 5578 if (mTargetApp.processName.equals("system")) { 5579 Slog.d(TAG, "system process agent - spinning a thread"); 5580 RestoreFileRunnable runner = new RestoreFileRunnable( 5581 mAgent, info, mPipes[0], token); 5582 new Thread(runner, "restore-sys-runner").start(); 5583 } else { 5584 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 5585 info.domain, info.path, info.mode, info.mtime, 5586 token, mBackupManagerBinder); 5587 } 5588 } 5589 } catch (IOException e) { 5590 // couldn't dup the socket for a process-local restore 5591 Slog.d(TAG, "Couldn't establish restore"); 5592 agentSuccess = false; 5593 okay = false; 5594 } catch (RemoteException e) { 5595 // whoops, remote entity went away. We'll eat the content 5596 // ourselves, then, and not copy it over. 5597 Slog.e(TAG, "Agent crashed during full restore"); 5598 agentSuccess = false; 5599 okay = false; 5600 } 5601 5602 // Copy over the data if the agent is still good 5603 if (okay) { 5604 if (MORE_DEBUG) { 5605 Slog.v(TAG, " copying to restore agent: " 5606 + toCopy + " bytes"); 5607 } 5608 boolean pipeOkay = true; 5609 FileOutputStream pipe = new FileOutputStream( 5610 mPipes[1].getFileDescriptor()); 5611 while (toCopy > 0) { 5612 int toRead = (toCopy > mBuffer.length) 5613 ? mBuffer.length : (int)toCopy; 5614 int nRead = instream.read(mBuffer, 0, toRead); 5615 if (nRead >= 0) mBytes += nRead; 5616 if (nRead <= 0) break; 5617 toCopy -= nRead; 5618 5619 // send it to the output pipe as long as things 5620 // are still good 5621 if (pipeOkay) { 5622 try { 5623 pipe.write(mBuffer, 0, nRead); 5624 } catch (IOException e) { 5625 Slog.e(TAG, "Failed to write to restore pipe: " 5626 + e.getMessage()); 5627 pipeOkay = false; 5628 } 5629 } 5630 } 5631 5632 // done sending that file! Now we just need to consume 5633 // the delta from info.size to the end of block. 5634 skipTarPadding(info.size, instream); 5635 5636 // and now that we've sent it all, wait for the remote 5637 // side to acknowledge receipt 5638 agentSuccess = waitUntilOperationComplete(token); 5639 } 5640 5641 // okay, if the remote end failed at any point, deal with 5642 // it by ignoring the rest of the restore on it 5643 if (!agentSuccess) { 5644 Slog.w(TAG, "Agent failure; ending restore"); 5645 mBackupHandler.removeMessages(MSG_TIMEOUT); 5646 tearDownPipes(); 5647 tearDownAgent(mTargetApp); 5648 mAgent = null; 5649 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5650 5651 // If this was a single-package restore, we halt immediately 5652 // with an agent error under these circumstances 5653 if (mOnlyPackage != null) { 5654 setResult(RestoreEngine.TARGET_FAILURE); 5655 setRunning(false); 5656 return false; 5657 } 5658 } 5659 } 5660 5661 // Problems setting up the agent communication, an explicitly 5662 // dropped file, or an already-ignored package: skip to the 5663 // next stream entry by reading and discarding this file. 5664 if (!okay) { 5665 if (MORE_DEBUG) Slog.d(TAG, "[discarding file content]"); 5666 long bytesToConsume = (info.size + 511) & ~511; 5667 while (bytesToConsume > 0) { 5668 int toRead = (bytesToConsume > mBuffer.length) 5669 ? mBuffer.length : (int)bytesToConsume; 5670 long nRead = instream.read(mBuffer, 0, toRead); 5671 if (nRead >= 0) mBytes += nRead; 5672 if (nRead <= 0) break; 5673 bytesToConsume -= nRead; 5674 } 5675 } 5676 } 5677 } 5678 } catch (IOException e) { 5679 if (DEBUG) Slog.w(TAG, "io exception on restore socket read: " + e.getMessage()); 5680 setResult(RestoreEngine.TRANSPORT_FAILURE); 5681 info = null; 5682 } 5683 5684 // If we got here we're either running smoothly or we've finished 5685 if (info == null) { 5686 if (MORE_DEBUG) { 5687 Slog.i(TAG, "No [more] data for this package; tearing down"); 5688 } 5689 tearDownPipes(); 5690 setRunning(false); 5691 if (mustKillAgent) { 5692 tearDownAgent(mTargetApp); 5693 } 5694 } 5695 return (info != null); 5696 } 5697 5698 void setUpPipes() throws IOException { 5699 mPipes = ParcelFileDescriptor.createPipe(); 5700 } 5701 5702 void tearDownPipes() { 5703 // Teardown might arise from the inline restore processing or from the asynchronous 5704 // timeout mechanism, and these might race. Make sure we don't try to close and 5705 // null out the pipes twice. 5706 synchronized (this) { 5707 if (mPipes != null) { 5708 try { 5709 mPipes[0].close(); 5710 mPipes[0] = null; 5711 mPipes[1].close(); 5712 mPipes[1] = null; 5713 } catch (IOException e) { 5714 Slog.w(TAG, "Couldn't close agent pipes", e); 5715 } 5716 mPipes = null; 5717 } 5718 } 5719 } 5720 5721 void tearDownAgent(ApplicationInfo app) { 5722 if (mAgent != null) { 5723 tearDownAgentAndKill(app); 5724 mAgent = null; 5725 } 5726 } 5727 5728 void handleTimeout() { 5729 tearDownPipes(); 5730 setResult(RestoreEngine.TARGET_FAILURE); 5731 setRunning(false); 5732 } 5733 5734 class RestoreInstallObserver extends PackageInstallObserver { 5735 final AtomicBoolean mDone = new AtomicBoolean(); 5736 String mPackageName; 5737 int mResult; 5738 5739 public void reset() { 5740 synchronized (mDone) { 5741 mDone.set(false); 5742 } 5743 } 5744 5745 public void waitForCompletion() { 5746 synchronized (mDone) { 5747 while (mDone.get() == false) { 5748 try { 5749 mDone.wait(); 5750 } catch (InterruptedException e) { } 5751 } 5752 } 5753 } 5754 5755 int getResult() { 5756 return mResult; 5757 } 5758 5759 @Override 5760 public void onPackageInstalled(String packageName, int returnCode, 5761 String msg, Bundle extras) { 5762 synchronized (mDone) { 5763 mResult = returnCode; 5764 mPackageName = packageName; 5765 mDone.set(true); 5766 mDone.notifyAll(); 5767 } 5768 } 5769 } 5770 5771 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 5772 final AtomicBoolean mDone = new AtomicBoolean(); 5773 int mResult; 5774 5775 public void reset() { 5776 synchronized (mDone) { 5777 mDone.set(false); 5778 } 5779 } 5780 5781 public void waitForCompletion() { 5782 synchronized (mDone) { 5783 while (mDone.get() == false) { 5784 try { 5785 mDone.wait(); 5786 } catch (InterruptedException e) { } 5787 } 5788 } 5789 } 5790 5791 @Override 5792 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 5793 synchronized (mDone) { 5794 mResult = returnCode; 5795 mDone.set(true); 5796 mDone.notifyAll(); 5797 } 5798 } 5799 } 5800 5801 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 5802 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 5803 5804 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 5805 boolean okay = true; 5806 5807 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 5808 5809 // The file content is an .apk file. Copy it out to a staging location and 5810 // attempt to install it. 5811 File apkFile = new File(mDataDir, info.packageName); 5812 try { 5813 FileOutputStream apkStream = new FileOutputStream(apkFile); 5814 byte[] buffer = new byte[32 * 1024]; 5815 long size = info.size; 5816 while (size > 0) { 5817 long toRead = (buffer.length < size) ? buffer.length : size; 5818 int didRead = instream.read(buffer, 0, (int)toRead); 5819 if (didRead >= 0) mBytes += didRead; 5820 apkStream.write(buffer, 0, didRead); 5821 size -= didRead; 5822 } 5823 apkStream.close(); 5824 5825 // make sure the installer can read it 5826 apkFile.setReadable(true, false); 5827 5828 // Now install it 5829 Uri packageUri = Uri.fromFile(apkFile); 5830 mInstallObserver.reset(); 5831 mPackageManager.installPackage(packageUri, mInstallObserver, 5832 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 5833 installerPackage); 5834 mInstallObserver.waitForCompletion(); 5835 5836 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 5837 // The only time we continue to accept install of data even if the 5838 // apk install failed is if we had already determined that we could 5839 // accept the data regardless. 5840 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 5841 okay = false; 5842 } 5843 } else { 5844 // Okay, the install succeeded. Make sure it was the right app. 5845 boolean uninstall = false; 5846 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 5847 Slog.w(TAG, "Restore stream claimed to include apk for " 5848 + info.packageName + " but apk was really " 5849 + mInstallObserver.mPackageName); 5850 // delete the package we just put in place; it might be fraudulent 5851 okay = false; 5852 uninstall = true; 5853 } else { 5854 try { 5855 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 5856 PackageManager.GET_SIGNATURES); 5857 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 5858 Slog.w(TAG, "Restore stream contains apk of package " 5859 + info.packageName + " but it disallows backup/restore"); 5860 okay = false; 5861 } else { 5862 // So far so good -- do the signatures match the manifest? 5863 Signature[] sigs = mManifestSignatures.get(info.packageName); 5864 if (signaturesMatch(sigs, pkg)) { 5865 // If this is a system-uid app without a declared backup agent, 5866 // don't restore any of the file data. 5867 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 5868 && (pkg.applicationInfo.backupAgentName == null)) { 5869 Slog.w(TAG, "Installed app " + info.packageName 5870 + " has restricted uid and no agent"); 5871 okay = false; 5872 } 5873 } else { 5874 Slog.w(TAG, "Installed app " + info.packageName 5875 + " signatures do not match restore manifest"); 5876 okay = false; 5877 uninstall = true; 5878 } 5879 } 5880 } catch (NameNotFoundException e) { 5881 Slog.w(TAG, "Install of package " + info.packageName 5882 + " succeeded but now not found"); 5883 okay = false; 5884 } 5885 } 5886 5887 // If we're not okay at this point, we need to delete the package 5888 // that we just installed. 5889 if (uninstall) { 5890 mDeleteObserver.reset(); 5891 mPackageManager.deletePackage(mInstallObserver.mPackageName, 5892 mDeleteObserver, 0); 5893 mDeleteObserver.waitForCompletion(); 5894 } 5895 } 5896 } catch (IOException e) { 5897 Slog.e(TAG, "Unable to transcribe restored apk for install"); 5898 okay = false; 5899 } finally { 5900 apkFile.delete(); 5901 } 5902 5903 return okay; 5904 } 5905 5906 // Given an actual file content size, consume the post-content padding mandated 5907 // by the tar format. 5908 void skipTarPadding(long size, InputStream instream) throws IOException { 5909 long partial = (size + 512) % 512; 5910 if (partial > 0) { 5911 final int needed = 512 - (int)partial; 5912 if (MORE_DEBUG) { 5913 Slog.i(TAG, "Skipping tar padding: " + needed + " bytes"); 5914 } 5915 byte[] buffer = new byte[needed]; 5916 if (readExactly(instream, buffer, 0, needed) == needed) { 5917 mBytes += needed; 5918 } else throw new IOException("Unexpected EOF in padding"); 5919 } 5920 } 5921 5922 // Read a widget metadata file, returning the restored blob 5923 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 5924 // Fail on suspiciously large widget dump files 5925 if (info.size > 64 * 1024) { 5926 throw new IOException("Metadata too big; corrupt? size=" + info.size); 5927 } 5928 5929 byte[] buffer = new byte[(int) info.size]; 5930 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5931 mBytes += info.size; 5932 } else throw new IOException("Unexpected EOF in widget data"); 5933 5934 String[] str = new String[1]; 5935 int offset = extractLine(buffer, 0, str); 5936 int version = Integer.parseInt(str[0]); 5937 if (version == BACKUP_MANIFEST_VERSION) { 5938 offset = extractLine(buffer, offset, str); 5939 final String pkg = str[0]; 5940 if (info.packageName.equals(pkg)) { 5941 // Data checks out -- the rest of the buffer is a concatenation of 5942 // binary blobs as described in the comment at writeAppWidgetData() 5943 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 5944 offset, buffer.length - offset); 5945 DataInputStream in = new DataInputStream(bin); 5946 while (bin.available() > 0) { 5947 int token = in.readInt(); 5948 int size = in.readInt(); 5949 if (size > 64 * 1024) { 5950 throw new IOException("Datum " 5951 + Integer.toHexString(token) 5952 + " too big; corrupt? size=" + info.size); 5953 } 5954 switch (token) { 5955 case BACKUP_WIDGET_METADATA_TOKEN: 5956 { 5957 if (MORE_DEBUG) { 5958 Slog.i(TAG, "Got widget metadata for " + info.packageName); 5959 } 5960 mWidgetData = new byte[size]; 5961 in.read(mWidgetData); 5962 break; 5963 } 5964 default: 5965 { 5966 if (DEBUG) { 5967 Slog.i(TAG, "Ignoring metadata blob " 5968 + Integer.toHexString(token) 5969 + " for " + info.packageName); 5970 } 5971 in.skipBytes(size); 5972 break; 5973 } 5974 } 5975 } 5976 } else { 5977 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 5978 + " but widget data for " + pkg); 5979 } 5980 } else { 5981 Slog.w(TAG, "Unsupported metadata version " + version); 5982 } 5983 } 5984 5985 // Returns a policy constant 5986 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 5987 throws IOException { 5988 // Fail on suspiciously large manifest files 5989 if (info.size > 64 * 1024) { 5990 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 5991 } 5992 5993 byte[] buffer = new byte[(int) info.size]; 5994 if (MORE_DEBUG) { 5995 Slog.i(TAG, " readAppManifest() looking for " + info.size + " bytes, " 5996 + mBytes + " already consumed"); 5997 } 5998 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5999 mBytes += info.size; 6000 } else throw new IOException("Unexpected EOF in manifest"); 6001 6002 RestorePolicy policy = RestorePolicy.IGNORE; 6003 String[] str = new String[1]; 6004 int offset = 0; 6005 6006 try { 6007 offset = extractLine(buffer, offset, str); 6008 int version = Integer.parseInt(str[0]); 6009 if (version == BACKUP_MANIFEST_VERSION) { 6010 offset = extractLine(buffer, offset, str); 6011 String manifestPackage = str[0]; 6012 // TODO: handle <original-package> 6013 if (manifestPackage.equals(info.packageName)) { 6014 offset = extractLine(buffer, offset, str); 6015 version = Integer.parseInt(str[0]); // app version 6016 offset = extractLine(buffer, offset, str); 6017 // This is the platform version, which we don't use, but we parse it 6018 // as a safety against corruption in the manifest. 6019 Integer.parseInt(str[0]); 6020 offset = extractLine(buffer, offset, str); 6021 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 6022 offset = extractLine(buffer, offset, str); 6023 boolean hasApk = str[0].equals("1"); 6024 offset = extractLine(buffer, offset, str); 6025 int numSigs = Integer.parseInt(str[0]); 6026 if (numSigs > 0) { 6027 Signature[] sigs = new Signature[numSigs]; 6028 for (int i = 0; i < numSigs; i++) { 6029 offset = extractLine(buffer, offset, str); 6030 sigs[i] = new Signature(str[0]); 6031 } 6032 mManifestSignatures.put(info.packageName, sigs); 6033 6034 // Okay, got the manifest info we need... 6035 try { 6036 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 6037 info.packageName, PackageManager.GET_SIGNATURES); 6038 // Fall through to IGNORE if the app explicitly disallows backup 6039 final int flags = pkgInfo.applicationInfo.flags; 6040 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 6041 // Restore system-uid-space packages only if they have 6042 // defined a custom backup agent 6043 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 6044 || (pkgInfo.applicationInfo.backupAgentName != null)) { 6045 // Verify signatures against any installed version; if they 6046 // don't match, then we fall though and ignore the data. The 6047 // signatureMatch() method explicitly ignores the signature 6048 // check for packages installed on the system partition, because 6049 // such packages are signed with the platform cert instead of 6050 // the app developer's cert, so they're different on every 6051 // device. 6052 if (signaturesMatch(sigs, pkgInfo)) { 6053 if (pkgInfo.versionCode >= version) { 6054 Slog.i(TAG, "Sig + version match; taking data"); 6055 policy = RestorePolicy.ACCEPT; 6056 } else { 6057 // The data is from a newer version of the app than 6058 // is presently installed. That means we can only 6059 // use it if the matching apk is also supplied. 6060 if (mAllowApks) { 6061 Slog.i(TAG, "Data version " + version 6062 + " is newer than installed version " 6063 + pkgInfo.versionCode 6064 + " - requiring apk"); 6065 policy = RestorePolicy.ACCEPT_IF_APK; 6066 } else { 6067 Slog.i(TAG, "Data requires newer version " 6068 + version + "; ignoring"); 6069 policy = RestorePolicy.IGNORE; 6070 } 6071 } 6072 } else { 6073 Slog.w(TAG, "Restore manifest signatures do not match " 6074 + "installed application for " + info.packageName); 6075 } 6076 } else { 6077 Slog.w(TAG, "Package " + info.packageName 6078 + " is system level with no agent"); 6079 } 6080 } else { 6081 if (DEBUG) Slog.i(TAG, "Restore manifest from " 6082 + info.packageName + " but allowBackup=false"); 6083 } 6084 } catch (NameNotFoundException e) { 6085 // Okay, the target app isn't installed. We can process 6086 // the restore properly only if the dataset provides the 6087 // apk file and we can successfully install it. 6088 if (mAllowApks) { 6089 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 6090 + " not installed; requiring apk in dataset"); 6091 policy = RestorePolicy.ACCEPT_IF_APK; 6092 } else { 6093 policy = RestorePolicy.IGNORE; 6094 } 6095 } 6096 6097 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 6098 Slog.i(TAG, "Cannot restore package " + info.packageName 6099 + " without the matching .apk"); 6100 } 6101 } else { 6102 Slog.i(TAG, "Missing signature on backed-up package " 6103 + info.packageName); 6104 } 6105 } else { 6106 Slog.i(TAG, "Expected package " + info.packageName 6107 + " but restore manifest claims " + manifestPackage); 6108 } 6109 } else { 6110 Slog.i(TAG, "Unknown restore manifest version " + version 6111 + " for package " + info.packageName); 6112 } 6113 } catch (NumberFormatException e) { 6114 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 6115 } catch (IllegalArgumentException e) { 6116 Slog.w(TAG, e.getMessage()); 6117 } 6118 6119 return policy; 6120 } 6121 6122 // Builds a line from a byte buffer starting at 'offset', and returns 6123 // the index of the next unconsumed data in the buffer. 6124 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 6125 final int end = buffer.length; 6126 if (offset >= end) throw new IOException("Incomplete data"); 6127 6128 int pos; 6129 for (pos = offset; pos < end; pos++) { 6130 byte c = buffer[pos]; 6131 // at LF we declare end of line, and return the next char as the 6132 // starting point for the next time through 6133 if (c == '\n') { 6134 break; 6135 } 6136 } 6137 outStr[0] = new String(buffer, offset, pos - offset); 6138 pos++; // may be pointing an extra byte past the end but that's okay 6139 return pos; 6140 } 6141 6142 void dumpFileMetadata(FileMetadata info) { 6143 if (MORE_DEBUG) { 6144 StringBuilder b = new StringBuilder(128); 6145 6146 // mode string 6147 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 6148 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 6149 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 6150 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 6151 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 6152 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 6153 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 6154 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 6155 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 6156 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 6157 b.append(String.format(" %9d ", info.size)); 6158 6159 Date stamp = new Date(info.mtime); 6160 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 6161 6162 b.append(info.packageName); 6163 b.append(" :: "); 6164 b.append(info.domain); 6165 b.append(" :: "); 6166 b.append(info.path); 6167 6168 Slog.i(TAG, b.toString()); 6169 } 6170 } 6171 6172 // Consume a tar file header block [sequence] and accumulate the relevant metadata 6173 FileMetadata readTarHeaders(InputStream instream) throws IOException { 6174 byte[] block = new byte[512]; 6175 FileMetadata info = null; 6176 6177 boolean gotHeader = readTarHeader(instream, block); 6178 if (gotHeader) { 6179 try { 6180 // okay, presume we're okay, and extract the various metadata 6181 info = new FileMetadata(); 6182 info.size = extractRadix(block, 124, 12, 8); 6183 info.mtime = extractRadix(block, 136, 12, 8); 6184 info.mode = extractRadix(block, 100, 8, 8); 6185 6186 info.path = extractString(block, 345, 155); // prefix 6187 String path = extractString(block, 0, 100); 6188 if (path.length() > 0) { 6189 if (info.path.length() > 0) info.path += '/'; 6190 info.path += path; 6191 } 6192 6193 // tar link indicator field: 1 byte at offset 156 in the header. 6194 int typeChar = block[156]; 6195 if (typeChar == 'x') { 6196 // pax extended header, so we need to read that 6197 gotHeader = readPaxExtendedHeader(instream, info); 6198 if (gotHeader) { 6199 // and after a pax extended header comes another real header -- read 6200 // that to find the real file type 6201 gotHeader = readTarHeader(instream, block); 6202 } 6203 if (!gotHeader) throw new IOException("Bad or missing pax header"); 6204 6205 typeChar = block[156]; 6206 } 6207 6208 switch (typeChar) { 6209 case '0': info.type = BackupAgent.TYPE_FILE; break; 6210 case '5': { 6211 info.type = BackupAgent.TYPE_DIRECTORY; 6212 if (info.size != 0) { 6213 Slog.w(TAG, "Directory entry with nonzero size in header"); 6214 info.size = 0; 6215 } 6216 break; 6217 } 6218 case 0: { 6219 // presume EOF 6220 if (MORE_DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 6221 return null; 6222 } 6223 default: { 6224 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 6225 throw new IOException("Unknown entity type " + typeChar); 6226 } 6227 } 6228 6229 // Parse out the path 6230 // 6231 // first: apps/shared/unrecognized 6232 if (FullBackup.SHARED_PREFIX.regionMatches(0, 6233 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 6234 // File in shared storage. !!! TODO: implement this. 6235 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 6236 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 6237 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 6238 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 6239 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 6240 info.path, 0, FullBackup.APPS_PREFIX.length())) { 6241 // App content! Parse out the package name and domain 6242 6243 // strip the apps/ prefix 6244 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 6245 6246 // extract the package name 6247 int slash = info.path.indexOf('/'); 6248 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 6249 info.packageName = info.path.substring(0, slash); 6250 info.path = info.path.substring(slash+1); 6251 6252 // if it's a manifest or metadata payload we're done, otherwise parse 6253 // out the domain into which the file will be restored 6254 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 6255 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 6256 slash = info.path.indexOf('/'); 6257 if (slash < 0) { 6258 throw new IOException("Illegal semantic path in non-manifest " 6259 + info.path); 6260 } 6261 info.domain = info.path.substring(0, slash); 6262 info.path = info.path.substring(slash + 1); 6263 } 6264 } 6265 } catch (IOException e) { 6266 if (DEBUG) { 6267 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 6268 if (MORE_DEBUG) { 6269 HEXLOG(block); 6270 } 6271 } 6272 throw e; 6273 } 6274 } 6275 return info; 6276 } 6277 6278 private boolean isRestorableFile(FileMetadata info) { 6279 if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { 6280 if (MORE_DEBUG) { 6281 Slog.i(TAG, "Dropping cache file path " + info.path); 6282 } 6283 return false; 6284 } 6285 6286 if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) { 6287 // It's possible this is "no-backup" dir contents in an archive stream 6288 // produced on a device running a version of the OS that predates that 6289 // API. Respect the no-backup intention and don't let the data get to 6290 // the app. 6291 if (info.path.startsWith("no_backup/")) { 6292 if (MORE_DEBUG) { 6293 Slog.i(TAG, "Dropping no_backup file path " + info.path); 6294 } 6295 return false; 6296 } 6297 } 6298 6299 // The path needs to be canonical 6300 if (info.path.contains("..") || info.path.contains("//")) { 6301 if (MORE_DEBUG) { 6302 Slog.w(TAG, "Dropping invalid path " + info.path); 6303 } 6304 return false; 6305 } 6306 6307 // Otherwise we think this file is good to go 6308 return true; 6309 } 6310 6311 private void HEXLOG(byte[] block) { 6312 int offset = 0; 6313 int todo = block.length; 6314 StringBuilder buf = new StringBuilder(64); 6315 while (todo > 0) { 6316 buf.append(String.format("%04x ", offset)); 6317 int numThisLine = (todo > 16) ? 16 : todo; 6318 for (int i = 0; i < numThisLine; i++) { 6319 buf.append(String.format("%02x ", block[offset+i])); 6320 } 6321 Slog.i("hexdump", buf.toString()); 6322 buf.setLength(0); 6323 todo -= numThisLine; 6324 offset += numThisLine; 6325 } 6326 } 6327 6328 // Read exactly the given number of bytes into a buffer at the stated offset. 6329 // Returns false if EOF is encountered before the requested number of bytes 6330 // could be read. 6331 int readExactly(InputStream in, byte[] buffer, int offset, int size) 6332 throws IOException { 6333 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 6334if (MORE_DEBUG) Slog.i(TAG, " ... readExactly(" + size + ") called"); 6335 int soFar = 0; 6336 while (soFar < size) { 6337 int nRead = in.read(buffer, offset + soFar, size - soFar); 6338 if (nRead <= 0) { 6339 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 6340 break; 6341 } 6342 soFar += nRead; 6343if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar)); 6344 } 6345 return soFar; 6346 } 6347 6348 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 6349 final int got = readExactly(instream, block, 0, 512); 6350 if (got == 0) return false; // Clean EOF 6351 if (got < 512) throw new IOException("Unable to read full block header"); 6352 mBytes += 512; 6353 return true; 6354 } 6355 6356 // overwrites 'info' fields based on the pax extended header 6357 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 6358 throws IOException { 6359 // We should never see a pax extended header larger than this 6360 if (info.size > 32*1024) { 6361 Slog.w(TAG, "Suspiciously large pax header size " + info.size 6362 + " - aborting"); 6363 throw new IOException("Sanity failure: pax header size " + info.size); 6364 } 6365 6366 // read whole blocks, not just the content size 6367 int numBlocks = (int)((info.size + 511) >> 9); 6368 byte[] data = new byte[numBlocks * 512]; 6369 if (readExactly(instream, data, 0, data.length) < data.length) { 6370 throw new IOException("Unable to read full pax header"); 6371 } 6372 mBytes += data.length; 6373 6374 final int contentSize = (int) info.size; 6375 int offset = 0; 6376 do { 6377 // extract the line at 'offset' 6378 int eol = offset+1; 6379 while (eol < contentSize && data[eol] != ' ') eol++; 6380 if (eol >= contentSize) { 6381 // error: we just hit EOD looking for the end of the size field 6382 throw new IOException("Invalid pax data"); 6383 } 6384 // eol points to the space between the count and the key 6385 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 6386 int key = eol + 1; // start of key=value 6387 eol = offset + linelen - 1; // trailing LF 6388 int value; 6389 for (value = key+1; data[value] != '=' && value <= eol; value++); 6390 if (value > eol) { 6391 throw new IOException("Invalid pax declaration"); 6392 } 6393 6394 // pax requires that key/value strings be in UTF-8 6395 String keyStr = new String(data, key, value-key, "UTF-8"); 6396 // -1 to strip the trailing LF 6397 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 6398 6399 if ("path".equals(keyStr)) { 6400 info.path = valStr; 6401 } else if ("size".equals(keyStr)) { 6402 info.size = Long.parseLong(valStr); 6403 } else { 6404 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 6405 } 6406 6407 offset += linelen; 6408 } while (offset < contentSize); 6409 6410 return true; 6411 } 6412 6413 long extractRadix(byte[] data, int offset, int maxChars, int radix) 6414 throws IOException { 6415 long value = 0; 6416 final int end = offset + maxChars; 6417 for (int i = offset; i < end; i++) { 6418 final byte b = data[i]; 6419 // Numeric fields in tar can terminate with either NUL or SPC 6420 if (b == 0 || b == ' ') break; 6421 if (b < '0' || b > ('0' + radix - 1)) { 6422 throw new IOException("Invalid number in header: '" + (char)b 6423 + "' for radix " + radix); 6424 } 6425 value = radix * value + (b - '0'); 6426 } 6427 return value; 6428 } 6429 6430 String extractString(byte[] data, int offset, int maxChars) throws IOException { 6431 final int end = offset + maxChars; 6432 int eos = offset; 6433 // tar string fields terminate early with a NUL 6434 while (eos < end && data[eos] != 0) eos++; 6435 return new String(data, offset, eos-offset, "US-ASCII"); 6436 } 6437 6438 void sendStartRestore() { 6439 if (mObserver != null) { 6440 try { 6441 mObserver.onStartRestore(); 6442 } catch (RemoteException e) { 6443 Slog.w(TAG, "full restore observer went away: startRestore"); 6444 mObserver = null; 6445 } 6446 } 6447 } 6448 6449 void sendOnRestorePackage(String name) { 6450 if (mObserver != null) { 6451 try { 6452 // TODO: use a more user-friendly name string 6453 mObserver.onRestorePackage(name); 6454 } catch (RemoteException e) { 6455 Slog.w(TAG, "full restore observer went away: restorePackage"); 6456 mObserver = null; 6457 } 6458 } 6459 } 6460 6461 void sendEndRestore() { 6462 if (mObserver != null) { 6463 try { 6464 mObserver.onEndRestore(); 6465 } catch (RemoteException e) { 6466 Slog.w(TAG, "full restore observer went away: endRestore"); 6467 mObserver = null; 6468 } 6469 } 6470 } 6471 } 6472 6473 // ***** end new engine class *** 6474 6475 // Used for synchronizing doRestoreFinished during adb restore 6476 class AdbRestoreFinishedLatch implements BackupRestoreTask { 6477 static final String TAG = "AdbRestoreFinishedLatch"; 6478 final CountDownLatch mLatch; 6479 6480 AdbRestoreFinishedLatch() { 6481 mLatch = new CountDownLatch(1); 6482 } 6483 6484 void await() { 6485 boolean latched = false; 6486 try { 6487 latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 6488 } catch (InterruptedException e) { 6489 Slog.w(TAG, "Interrupted!"); 6490 } 6491 } 6492 6493 @Override 6494 public void execute() { 6495 // Unused 6496 } 6497 6498 @Override 6499 public void operationComplete(long result) { 6500 if (MORE_DEBUG) { 6501 Slog.w(TAG, "adb onRestoreFinished() complete"); 6502 } 6503 mLatch.countDown(); 6504 } 6505 6506 @Override 6507 public void handleTimeout() { 6508 if (DEBUG) { 6509 Slog.w(TAG, "adb onRestoreFinished() timed out"); 6510 } 6511 mLatch.countDown(); 6512 } 6513 } 6514 6515 class PerformAdbRestoreTask implements Runnable { 6516 ParcelFileDescriptor mInputFile; 6517 String mCurrentPassword; 6518 String mDecryptPassword; 6519 IFullBackupRestoreObserver mObserver; 6520 AtomicBoolean mLatchObject; 6521 IBackupAgent mAgent; 6522 String mAgentPackage; 6523 ApplicationInfo mTargetApp; 6524 FullBackupObbConnection mObbConnection = null; 6525 ParcelFileDescriptor[] mPipes = null; 6526 byte[] mWidgetData = null; 6527 6528 long mBytes; 6529 6530 // Runner that can be placed on a separate thread to do in-process invocation 6531 // of the "restore finished" API asynchronously. Used by adb restore. 6532 class RestoreFinishedRunnable implements Runnable { 6533 final IBackupAgent mAgent; 6534 final int mToken; 6535 6536 RestoreFinishedRunnable(IBackupAgent agent, int token) { 6537 mAgent = agent; 6538 mToken = token; 6539 } 6540 6541 @Override 6542 public void run() { 6543 try { 6544 mAgent.doRestoreFinished(mToken, mBackupManagerBinder); 6545 } catch (RemoteException e) { 6546 // never happens; this is used only for local binder calls 6547 } 6548 } 6549 } 6550 6551 // possible handling states for a given package in the restore dataset 6552 final HashMap<String, RestorePolicy> mPackagePolicies 6553 = new HashMap<String, RestorePolicy>(); 6554 6555 // installer package names for each encountered app, derived from the manifests 6556 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 6557 6558 // Signatures for a given package found in its manifest file 6559 final HashMap<String, Signature[]> mManifestSignatures 6560 = new HashMap<String, Signature[]>(); 6561 6562 // Packages we've already wiped data on when restoring their first file 6563 final HashSet<String> mClearedPackages = new HashSet<String>(); 6564 6565 PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword, 6566 IFullBackupRestoreObserver observer, AtomicBoolean latch) { 6567 mInputFile = fd; 6568 mCurrentPassword = curPassword; 6569 mDecryptPassword = decryptPassword; 6570 mObserver = observer; 6571 mLatchObject = latch; 6572 mAgent = null; 6573 mAgentPackage = null; 6574 mTargetApp = null; 6575 mObbConnection = new FullBackupObbConnection(); 6576 6577 // Which packages we've already wiped data on. We prepopulate this 6578 // with a whitelist of packages known to be unclearable. 6579 mClearedPackages.add("android"); 6580 mClearedPackages.add(SETTINGS_PACKAGE); 6581 } 6582 6583 class RestoreFileRunnable implements Runnable { 6584 IBackupAgent mAgent; 6585 FileMetadata mInfo; 6586 ParcelFileDescriptor mSocket; 6587 int mToken; 6588 6589 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 6590 ParcelFileDescriptor socket, int token) throws IOException { 6591 mAgent = agent; 6592 mInfo = info; 6593 mToken = token; 6594 6595 // This class is used strictly for process-local binder invocations. The 6596 // semantics of ParcelFileDescriptor differ in this case; in particular, we 6597 // do not automatically get a 'dup'ed descriptor that we can can continue 6598 // to use asynchronously from the caller. So, we make sure to dup it ourselves 6599 // before proceeding to do the restore. 6600 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 6601 } 6602 6603 @Override 6604 public void run() { 6605 try { 6606 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 6607 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 6608 mToken, mBackupManagerBinder); 6609 } catch (RemoteException e) { 6610 // never happens; this is used strictly for local binder calls 6611 } 6612 } 6613 } 6614 6615 @Override 6616 public void run() { 6617 Slog.i(TAG, "--- Performing full-dataset restore ---"); 6618 mObbConnection.establish(); 6619 sendStartRestore(); 6620 6621 // Are we able to restore shared-storage data? 6622 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 6623 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT); 6624 } 6625 6626 FileInputStream rawInStream = null; 6627 DataInputStream rawDataIn = null; 6628 try { 6629 if (!backupPasswordMatches(mCurrentPassword)) { 6630 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 6631 return; 6632 } 6633 6634 mBytes = 0; 6635 byte[] buffer = new byte[32 * 1024]; 6636 rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); 6637 rawDataIn = new DataInputStream(rawInStream); 6638 6639 // First, parse out the unencrypted/uncompressed header 6640 boolean compressed = false; 6641 InputStream preCompressStream = rawInStream; 6642 final InputStream in; 6643 6644 boolean okay = false; 6645 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length(); 6646 byte[] streamHeader = new byte[headerLen]; 6647 rawDataIn.readFully(streamHeader); 6648 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8"); 6649 if (Arrays.equals(magicBytes, streamHeader)) { 6650 // okay, header looks good. now parse out the rest of the fields. 6651 String s = readHeaderLine(rawInStream); 6652 final int archiveVersion = Integer.parseInt(s); 6653 if (archiveVersion <= BACKUP_FILE_VERSION) { 6654 // okay, it's a version we recognize. if it's version 1, we may need 6655 // to try two different PBKDF2 regimes to compare checksums. 6656 final boolean pbkdf2Fallback = (archiveVersion == 1); 6657 6658 s = readHeaderLine(rawInStream); 6659 compressed = (Integer.parseInt(s) != 0); 6660 s = readHeaderLine(rawInStream); 6661 if (s.equals("none")) { 6662 // no more header to parse; we're good to go 6663 okay = true; 6664 } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) { 6665 preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback, 6666 rawInStream); 6667 if (preCompressStream != null) { 6668 okay = true; 6669 } 6670 } else Slog.w(TAG, "Archive is encrypted but no password given"); 6671 } else Slog.w(TAG, "Wrong header version: " + s); 6672 } else Slog.w(TAG, "Didn't read the right header magic"); 6673 6674 if (!okay) { 6675 Slog.w(TAG, "Invalid restore data; aborting."); 6676 return; 6677 } 6678 6679 // okay, use the right stream layer based on compression 6680 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream; 6681 6682 boolean didRestore; 6683 do { 6684 didRestore = restoreOneFile(in, buffer); 6685 } while (didRestore); 6686 6687 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); 6688 } catch (IOException e) { 6689 Slog.e(TAG, "Unable to read restore input"); 6690 } finally { 6691 tearDownPipes(); 6692 tearDownAgent(mTargetApp, true); 6693 6694 try { 6695 if (rawDataIn != null) rawDataIn.close(); 6696 if (rawInStream != null) rawInStream.close(); 6697 mInputFile.close(); 6698 } catch (IOException e) { 6699 Slog.w(TAG, "Close of restore data pipe threw", e); 6700 /* nothing we can do about this */ 6701 } 6702 synchronized (mCurrentOpLock) { 6703 mCurrentOperations.clear(); 6704 } 6705 synchronized (mLatchObject) { 6706 mLatchObject.set(true); 6707 mLatchObject.notifyAll(); 6708 } 6709 mObbConnection.tearDown(); 6710 sendEndRestore(); 6711 Slog.d(TAG, "Full restore pass complete."); 6712 mWakelock.release(); 6713 } 6714 } 6715 6716 String readHeaderLine(InputStream in) throws IOException { 6717 int c; 6718 StringBuilder buffer = new StringBuilder(80); 6719 while ((c = in.read()) >= 0) { 6720 if (c == '\n') break; // consume and discard the newlines 6721 buffer.append((char)c); 6722 } 6723 return buffer.toString(); 6724 } 6725 6726 InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt, 6727 int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream, 6728 boolean doLog) { 6729 InputStream result = null; 6730 6731 try { 6732 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 6733 SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt, 6734 rounds); 6735 byte[] IV = hexToByteArray(userIvHex); 6736 IvParameterSpec ivSpec = new IvParameterSpec(IV); 6737 c.init(Cipher.DECRYPT_MODE, 6738 new SecretKeySpec(userKey.getEncoded(), "AES"), 6739 ivSpec); 6740 byte[] mkCipher = hexToByteArray(masterKeyBlobHex); 6741 byte[] mkBlob = c.doFinal(mkCipher); 6742 6743 // first, the master key IV 6744 int offset = 0; 6745 int len = mkBlob[offset++]; 6746 IV = Arrays.copyOfRange(mkBlob, offset, offset + len); 6747 offset += len; 6748 // then the master key itself 6749 len = mkBlob[offset++]; 6750 byte[] mk = Arrays.copyOfRange(mkBlob, 6751 offset, offset + len); 6752 offset += len; 6753 // and finally the master key checksum hash 6754 len = mkBlob[offset++]; 6755 byte[] mkChecksum = Arrays.copyOfRange(mkBlob, 6756 offset, offset + len); 6757 6758 // now validate the decrypted master key against the checksum 6759 byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds); 6760 if (Arrays.equals(calculatedCk, mkChecksum)) { 6761 ivSpec = new IvParameterSpec(IV); 6762 c.init(Cipher.DECRYPT_MODE, 6763 new SecretKeySpec(mk, "AES"), 6764 ivSpec); 6765 // Only if all of the above worked properly will 'result' be assigned 6766 result = new CipherInputStream(rawInStream, c); 6767 } else if (doLog) Slog.w(TAG, "Incorrect password"); 6768 } catch (InvalidAlgorithmParameterException e) { 6769 if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e); 6770 } catch (BadPaddingException e) { 6771 // This case frequently occurs when the wrong password is used to decrypt 6772 // the master key. Use the identical "incorrect password" log text as is 6773 // used in the checksum failure log in order to avoid providing additional 6774 // information to an attacker. 6775 if (doLog) Slog.w(TAG, "Incorrect password"); 6776 } catch (IllegalBlockSizeException e) { 6777 if (doLog) Slog.w(TAG, "Invalid block size in master key"); 6778 } catch (NoSuchAlgorithmException e) { 6779 if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!"); 6780 } catch (NoSuchPaddingException e) { 6781 if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!"); 6782 } catch (InvalidKeyException e) { 6783 if (doLog) Slog.w(TAG, "Illegal password; aborting"); 6784 } 6785 6786 return result; 6787 } 6788 6789 InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback, 6790 InputStream rawInStream) { 6791 InputStream result = null; 6792 try { 6793 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) { 6794 6795 String userSaltHex = readHeaderLine(rawInStream); // 5 6796 byte[] userSalt = hexToByteArray(userSaltHex); 6797 6798 String ckSaltHex = readHeaderLine(rawInStream); // 6 6799 byte[] ckSalt = hexToByteArray(ckSaltHex); 6800 6801 int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 6802 String userIvHex = readHeaderLine(rawInStream); // 8 6803 6804 String masterKeyBlobHex = readHeaderLine(rawInStream); // 9 6805 6806 // decrypt the master key blob 6807 result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt, 6808 rounds, userIvHex, masterKeyBlobHex, rawInStream, false); 6809 if (result == null && pbkdf2Fallback) { 6810 result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt, 6811 rounds, userIvHex, masterKeyBlobHex, rawInStream, true); 6812 } 6813 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName); 6814 } catch (NumberFormatException e) { 6815 Slog.w(TAG, "Can't parse restore data header"); 6816 } catch (IOException e) { 6817 Slog.w(TAG, "Can't read input header"); 6818 } 6819 6820 return result; 6821 } 6822 6823 boolean restoreOneFile(InputStream instream, byte[] buffer) { 6824 FileMetadata info; 6825 try { 6826 info = readTarHeaders(instream); 6827 if (info != null) { 6828 if (MORE_DEBUG) { 6829 dumpFileMetadata(info); 6830 } 6831 6832 final String pkg = info.packageName; 6833 if (!pkg.equals(mAgentPackage)) { 6834 // okay, change in package; set up our various 6835 // bookkeeping if we haven't seen it yet 6836 if (!mPackagePolicies.containsKey(pkg)) { 6837 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6838 } 6839 6840 // Clean up the previous agent relationship if necessary, 6841 // and let the observer know we're considering a new app. 6842 if (mAgent != null) { 6843 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 6844 // Now we're really done 6845 tearDownPipes(); 6846 tearDownAgent(mTargetApp, true); 6847 mTargetApp = null; 6848 mAgentPackage = null; 6849 } 6850 } 6851 6852 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 6853 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 6854 mPackageInstallers.put(pkg, info.installerPackageName); 6855 // We've read only the manifest content itself at this point, 6856 // so consume the footer before looping around to the next 6857 // input file 6858 skipTarPadding(info.size, instream); 6859 sendOnRestorePackage(pkg); 6860 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 6861 // Metadata blobs! 6862 readMetadata(info, instream); 6863 skipTarPadding(info.size, instream); 6864 } else { 6865 // Non-manifest, so it's actual file data. Is this a package 6866 // we're ignoring? 6867 boolean okay = true; 6868 RestorePolicy policy = mPackagePolicies.get(pkg); 6869 switch (policy) { 6870 case IGNORE: 6871 okay = false; 6872 break; 6873 6874 case ACCEPT_IF_APK: 6875 // If we're in accept-if-apk state, then the first file we 6876 // see MUST be the apk. 6877 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 6878 if (DEBUG) Slog.d(TAG, "APK file; installing"); 6879 // Try to install the app. 6880 String installerName = mPackageInstallers.get(pkg); 6881 okay = installApk(info, installerName, instream); 6882 // good to go; promote to ACCEPT 6883 mPackagePolicies.put(pkg, (okay) 6884 ? RestorePolicy.ACCEPT 6885 : RestorePolicy.IGNORE); 6886 // At this point we've consumed this file entry 6887 // ourselves, so just strip the tar footer and 6888 // go on to the next file in the input stream 6889 skipTarPadding(info.size, instream); 6890 return true; 6891 } else { 6892 // File data before (or without) the apk. We can't 6893 // handle it coherently in this case so ignore it. 6894 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6895 okay = false; 6896 } 6897 break; 6898 6899 case ACCEPT: 6900 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 6901 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 6902 // we can take the data without the apk, so we 6903 // *want* to do so. skip the apk by declaring this 6904 // one file not-okay without changing the restore 6905 // policy for the package. 6906 okay = false; 6907 } 6908 break; 6909 6910 default: 6911 // Something has gone dreadfully wrong when determining 6912 // the restore policy from the manifest. Ignore the 6913 // rest of this package's data. 6914 Slog.e(TAG, "Invalid policy from manifest"); 6915 okay = false; 6916 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6917 break; 6918 } 6919 6920 // The path needs to be canonical 6921 if (info.path.contains("..") || info.path.contains("//")) { 6922 if (MORE_DEBUG) { 6923 Slog.w(TAG, "Dropping invalid path " + info.path); 6924 } 6925 okay = false; 6926 } 6927 6928 // If the policy is satisfied, go ahead and set up to pipe the 6929 // data to the agent. 6930 if (DEBUG && okay && mAgent != null) { 6931 Slog.i(TAG, "Reusing existing agent instance"); 6932 } 6933 if (okay && mAgent == null) { 6934 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 6935 6936 try { 6937 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 6938 6939 // If we haven't sent any data to this app yet, we probably 6940 // need to clear it first. Check that. 6941 if (!mClearedPackages.contains(pkg)) { 6942 // apps with their own backup agents are 6943 // responsible for coherently managing a full 6944 // restore. 6945 if (mTargetApp.backupAgentName == null) { 6946 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 6947 clearApplicationDataSynchronous(pkg); 6948 } else { 6949 if (DEBUG) Slog.d(TAG, "backup agent (" 6950 + mTargetApp.backupAgentName + ") => no clear"); 6951 } 6952 mClearedPackages.add(pkg); 6953 } else { 6954 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 6955 } 6956 6957 // All set; now set up the IPC and launch the agent 6958 setUpPipes(); 6959 mAgent = bindToAgentSynchronous(mTargetApp, 6960 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 6961 mAgentPackage = pkg; 6962 } catch (IOException e) { 6963 // fall through to error handling 6964 } catch (NameNotFoundException e) { 6965 // fall through to error handling 6966 } 6967 6968 if (mAgent == null) { 6969 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 6970 okay = false; 6971 tearDownPipes(); 6972 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6973 } 6974 } 6975 6976 // Sanity check: make sure we never give data to the wrong app. This 6977 // should never happen but a little paranoia here won't go amiss. 6978 if (okay && !pkg.equals(mAgentPackage)) { 6979 Slog.e(TAG, "Restoring data for " + pkg 6980 + " but agent is for " + mAgentPackage); 6981 okay = false; 6982 } 6983 6984 // At this point we have an agent ready to handle the full 6985 // restore data as well as a pipe for sending data to 6986 // that agent. Tell the agent to start reading from the 6987 // pipe. 6988 if (okay) { 6989 boolean agentSuccess = true; 6990 long toCopy = info.size; 6991 final int token = generateToken(); 6992 try { 6993 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 6994 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 6995 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 6996 + " : " + info.path); 6997 mObbConnection.restoreObbFile(pkg, mPipes[0], 6998 info.size, info.type, info.path, info.mode, 6999 info.mtime, token, mBackupManagerBinder); 7000 } else { 7001 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 7002 + info.path); 7003 // fire up the app's agent listening on the socket. If 7004 // the agent is running in the system process we can't 7005 // just invoke it asynchronously, so we provide a thread 7006 // for it here. 7007 if (mTargetApp.processName.equals("system")) { 7008 Slog.d(TAG, "system process agent - spinning a thread"); 7009 RestoreFileRunnable runner = new RestoreFileRunnable( 7010 mAgent, info, mPipes[0], token); 7011 new Thread(runner, "restore-sys-runner").start(); 7012 } else { 7013 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 7014 info.domain, info.path, info.mode, info.mtime, 7015 token, mBackupManagerBinder); 7016 } 7017 } 7018 } catch (IOException e) { 7019 // couldn't dup the socket for a process-local restore 7020 Slog.d(TAG, "Couldn't establish restore"); 7021 agentSuccess = false; 7022 okay = false; 7023 } catch (RemoteException e) { 7024 // whoops, remote entity went away. We'll eat the content 7025 // ourselves, then, and not copy it over. 7026 Slog.e(TAG, "Agent crashed during full restore"); 7027 agentSuccess = false; 7028 okay = false; 7029 } 7030 7031 // Copy over the data if the agent is still good 7032 if (okay) { 7033 boolean pipeOkay = true; 7034 FileOutputStream pipe = new FileOutputStream( 7035 mPipes[1].getFileDescriptor()); 7036 while (toCopy > 0) { 7037 int toRead = (toCopy > buffer.length) 7038 ? buffer.length : (int)toCopy; 7039 int nRead = instream.read(buffer, 0, toRead); 7040 if (nRead >= 0) mBytes += nRead; 7041 if (nRead <= 0) break; 7042 toCopy -= nRead; 7043 7044 // send it to the output pipe as long as things 7045 // are still good 7046 if (pipeOkay) { 7047 try { 7048 pipe.write(buffer, 0, nRead); 7049 } catch (IOException e) { 7050 Slog.e(TAG, "Failed to write to restore pipe", e); 7051 pipeOkay = false; 7052 } 7053 } 7054 } 7055 7056 // done sending that file! Now we just need to consume 7057 // the delta from info.size to the end of block. 7058 skipTarPadding(info.size, instream); 7059 7060 // and now that we've sent it all, wait for the remote 7061 // side to acknowledge receipt 7062 agentSuccess = waitUntilOperationComplete(token); 7063 } 7064 7065 // okay, if the remote end failed at any point, deal with 7066 // it by ignoring the rest of the restore on it 7067 if (!agentSuccess) { 7068 if (DEBUG) { 7069 Slog.d(TAG, "Agent failure restoring " + pkg + "; now ignoring"); 7070 } 7071 mBackupHandler.removeMessages(MSG_TIMEOUT); 7072 tearDownPipes(); 7073 tearDownAgent(mTargetApp, false); 7074 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 7075 } 7076 } 7077 7078 // Problems setting up the agent communication, or an already- 7079 // ignored package: skip to the next tar stream entry by 7080 // reading and discarding this file. 7081 if (!okay) { 7082 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 7083 long bytesToConsume = (info.size + 511) & ~511; 7084 while (bytesToConsume > 0) { 7085 int toRead = (bytesToConsume > buffer.length) 7086 ? buffer.length : (int)bytesToConsume; 7087 long nRead = instream.read(buffer, 0, toRead); 7088 if (nRead >= 0) mBytes += nRead; 7089 if (nRead <= 0) break; 7090 bytesToConsume -= nRead; 7091 } 7092 } 7093 } 7094 } 7095 } catch (IOException e) { 7096 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 7097 // treat as EOF 7098 info = null; 7099 } 7100 7101 return (info != null); 7102 } 7103 7104 void setUpPipes() throws IOException { 7105 mPipes = ParcelFileDescriptor.createPipe(); 7106 } 7107 7108 void tearDownPipes() { 7109 if (mPipes != null) { 7110 try { 7111 mPipes[0].close(); 7112 mPipes[0] = null; 7113 mPipes[1].close(); 7114 mPipes[1] = null; 7115 } catch (IOException e) { 7116 Slog.w(TAG, "Couldn't close agent pipes", e); 7117 } 7118 mPipes = null; 7119 } 7120 } 7121 7122 void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) { 7123 if (mAgent != null) { 7124 try { 7125 // In the adb restore case, we do restore-finished here 7126 if (doRestoreFinished) { 7127 final int token = generateToken(); 7128 final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(); 7129 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, latch); 7130 if (mTargetApp.processName.equals("system")) { 7131 if (MORE_DEBUG) { 7132 Slog.d(TAG, "system agent - restoreFinished on thread"); 7133 } 7134 Runnable runner = new RestoreFinishedRunnable(mAgent, token); 7135 new Thread(runner, "restore-sys-finished-runner").start(); 7136 } else { 7137 mAgent.doRestoreFinished(token, mBackupManagerBinder); 7138 } 7139 7140 latch.await(); 7141 } 7142 7143 // unbind and tidy up even on timeout or failure, just in case 7144 mActivityManager.unbindBackupAgent(app); 7145 7146 // The agent was running with a stub Application object, so shut it down. 7147 // !!! We hardcode the confirmation UI's package name here rather than use a 7148 // manifest flag! TODO something less direct. 7149 if (app.uid >= Process.FIRST_APPLICATION_UID 7150 && !app.packageName.equals("com.android.backupconfirm")) { 7151 if (DEBUG) Slog.d(TAG, "Killing host process"); 7152 mActivityManager.killApplicationProcess(app.processName, app.uid); 7153 } else { 7154 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 7155 } 7156 } catch (RemoteException e) { 7157 Slog.d(TAG, "Lost app trying to shut down"); 7158 } 7159 mAgent = null; 7160 } 7161 } 7162 7163 class RestoreInstallObserver extends PackageInstallObserver { 7164 final AtomicBoolean mDone = new AtomicBoolean(); 7165 String mPackageName; 7166 int mResult; 7167 7168 public void reset() { 7169 synchronized (mDone) { 7170 mDone.set(false); 7171 } 7172 } 7173 7174 public void waitForCompletion() { 7175 synchronized (mDone) { 7176 while (mDone.get() == false) { 7177 try { 7178 mDone.wait(); 7179 } catch (InterruptedException e) { } 7180 } 7181 } 7182 } 7183 7184 int getResult() { 7185 return mResult; 7186 } 7187 7188 @Override 7189 public void onPackageInstalled(String packageName, int returnCode, 7190 String msg, Bundle extras) { 7191 synchronized (mDone) { 7192 mResult = returnCode; 7193 mPackageName = packageName; 7194 mDone.set(true); 7195 mDone.notifyAll(); 7196 } 7197 } 7198 } 7199 7200 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 7201 final AtomicBoolean mDone = new AtomicBoolean(); 7202 int mResult; 7203 7204 public void reset() { 7205 synchronized (mDone) { 7206 mDone.set(false); 7207 } 7208 } 7209 7210 public void waitForCompletion() { 7211 synchronized (mDone) { 7212 while (mDone.get() == false) { 7213 try { 7214 mDone.wait(); 7215 } catch (InterruptedException e) { } 7216 } 7217 } 7218 } 7219 7220 @Override 7221 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 7222 synchronized (mDone) { 7223 mResult = returnCode; 7224 mDone.set(true); 7225 mDone.notifyAll(); 7226 } 7227 } 7228 } 7229 7230 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 7231 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 7232 7233 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 7234 boolean okay = true; 7235 7236 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 7237 7238 // The file content is an .apk file. Copy it out to a staging location and 7239 // attempt to install it. 7240 File apkFile = new File(mDataDir, info.packageName); 7241 try { 7242 FileOutputStream apkStream = new FileOutputStream(apkFile); 7243 byte[] buffer = new byte[32 * 1024]; 7244 long size = info.size; 7245 while (size > 0) { 7246 long toRead = (buffer.length < size) ? buffer.length : size; 7247 int didRead = instream.read(buffer, 0, (int)toRead); 7248 if (didRead >= 0) mBytes += didRead; 7249 apkStream.write(buffer, 0, didRead); 7250 size -= didRead; 7251 } 7252 apkStream.close(); 7253 7254 // make sure the installer can read it 7255 apkFile.setReadable(true, false); 7256 7257 // Now install it 7258 Uri packageUri = Uri.fromFile(apkFile); 7259 mInstallObserver.reset(); 7260 mPackageManager.installPackage(packageUri, mInstallObserver, 7261 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 7262 installerPackage); 7263 mInstallObserver.waitForCompletion(); 7264 7265 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 7266 // The only time we continue to accept install of data even if the 7267 // apk install failed is if we had already determined that we could 7268 // accept the data regardless. 7269 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 7270 okay = false; 7271 } 7272 } else { 7273 // Okay, the install succeeded. Make sure it was the right app. 7274 boolean uninstall = false; 7275 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 7276 Slog.w(TAG, "Restore stream claimed to include apk for " 7277 + info.packageName + " but apk was really " 7278 + mInstallObserver.mPackageName); 7279 // delete the package we just put in place; it might be fraudulent 7280 okay = false; 7281 uninstall = true; 7282 } else { 7283 try { 7284 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 7285 PackageManager.GET_SIGNATURES); 7286 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 7287 Slog.w(TAG, "Restore stream contains apk of package " 7288 + info.packageName + " but it disallows backup/restore"); 7289 okay = false; 7290 } else { 7291 // So far so good -- do the signatures match the manifest? 7292 Signature[] sigs = mManifestSignatures.get(info.packageName); 7293 if (signaturesMatch(sigs, pkg)) { 7294 // If this is a system-uid app without a declared backup agent, 7295 // don't restore any of the file data. 7296 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 7297 && (pkg.applicationInfo.backupAgentName == null)) { 7298 Slog.w(TAG, "Installed app " + info.packageName 7299 + " has restricted uid and no agent"); 7300 okay = false; 7301 } 7302 } else { 7303 Slog.w(TAG, "Installed app " + info.packageName 7304 + " signatures do not match restore manifest"); 7305 okay = false; 7306 uninstall = true; 7307 } 7308 } 7309 } catch (NameNotFoundException e) { 7310 Slog.w(TAG, "Install of package " + info.packageName 7311 + " succeeded but now not found"); 7312 okay = false; 7313 } 7314 } 7315 7316 // If we're not okay at this point, we need to delete the package 7317 // that we just installed. 7318 if (uninstall) { 7319 mDeleteObserver.reset(); 7320 mPackageManager.deletePackage(mInstallObserver.mPackageName, 7321 mDeleteObserver, 0); 7322 mDeleteObserver.waitForCompletion(); 7323 } 7324 } 7325 } catch (IOException e) { 7326 Slog.e(TAG, "Unable to transcribe restored apk for install"); 7327 okay = false; 7328 } finally { 7329 apkFile.delete(); 7330 } 7331 7332 return okay; 7333 } 7334 7335 // Given an actual file content size, consume the post-content padding mandated 7336 // by the tar format. 7337 void skipTarPadding(long size, InputStream instream) throws IOException { 7338 long partial = (size + 512) % 512; 7339 if (partial > 0) { 7340 final int needed = 512 - (int)partial; 7341 byte[] buffer = new byte[needed]; 7342 if (readExactly(instream, buffer, 0, needed) == needed) { 7343 mBytes += needed; 7344 } else throw new IOException("Unexpected EOF in padding"); 7345 } 7346 } 7347 7348 // Read a widget metadata file, returning the restored blob 7349 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 7350 // Fail on suspiciously large widget dump files 7351 if (info.size > 64 * 1024) { 7352 throw new IOException("Metadata too big; corrupt? size=" + info.size); 7353 } 7354 7355 byte[] buffer = new byte[(int) info.size]; 7356 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 7357 mBytes += info.size; 7358 } else throw new IOException("Unexpected EOF in widget data"); 7359 7360 String[] str = new String[1]; 7361 int offset = extractLine(buffer, 0, str); 7362 int version = Integer.parseInt(str[0]); 7363 if (version == BACKUP_MANIFEST_VERSION) { 7364 offset = extractLine(buffer, offset, str); 7365 final String pkg = str[0]; 7366 if (info.packageName.equals(pkg)) { 7367 // Data checks out -- the rest of the buffer is a concatenation of 7368 // binary blobs as described in the comment at writeAppWidgetData() 7369 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 7370 offset, buffer.length - offset); 7371 DataInputStream in = new DataInputStream(bin); 7372 while (bin.available() > 0) { 7373 int token = in.readInt(); 7374 int size = in.readInt(); 7375 if (size > 64 * 1024) { 7376 throw new IOException("Datum " 7377 + Integer.toHexString(token) 7378 + " too big; corrupt? size=" + info.size); 7379 } 7380 switch (token) { 7381 case BACKUP_WIDGET_METADATA_TOKEN: 7382 { 7383 if (MORE_DEBUG) { 7384 Slog.i(TAG, "Got widget metadata for " + info.packageName); 7385 } 7386 mWidgetData = new byte[size]; 7387 in.read(mWidgetData); 7388 break; 7389 } 7390 default: 7391 { 7392 if (DEBUG) { 7393 Slog.i(TAG, "Ignoring metadata blob " 7394 + Integer.toHexString(token) 7395 + " for " + info.packageName); 7396 } 7397 in.skipBytes(size); 7398 break; 7399 } 7400 } 7401 } 7402 } else { 7403 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 7404 + " but widget data for " + pkg); 7405 } 7406 } else { 7407 Slog.w(TAG, "Unsupported metadata version " + version); 7408 } 7409 } 7410 7411 // Returns a policy constant; takes a buffer arg to reduce memory churn 7412 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 7413 throws IOException { 7414 // Fail on suspiciously large manifest files 7415 if (info.size > 64 * 1024) { 7416 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 7417 } 7418 7419 byte[] buffer = new byte[(int) info.size]; 7420 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 7421 mBytes += info.size; 7422 } else throw new IOException("Unexpected EOF in manifest"); 7423 7424 RestorePolicy policy = RestorePolicy.IGNORE; 7425 String[] str = new String[1]; 7426 int offset = 0; 7427 7428 try { 7429 offset = extractLine(buffer, offset, str); 7430 int version = Integer.parseInt(str[0]); 7431 if (version == BACKUP_MANIFEST_VERSION) { 7432 offset = extractLine(buffer, offset, str); 7433 String manifestPackage = str[0]; 7434 // TODO: handle <original-package> 7435 if (manifestPackage.equals(info.packageName)) { 7436 offset = extractLine(buffer, offset, str); 7437 version = Integer.parseInt(str[0]); // app version 7438 offset = extractLine(buffer, offset, str); 7439 // This is the platform version, which we don't use, but we parse it 7440 // as a safety against corruption in the manifest. 7441 Integer.parseInt(str[0]); 7442 offset = extractLine(buffer, offset, str); 7443 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 7444 offset = extractLine(buffer, offset, str); 7445 boolean hasApk = str[0].equals("1"); 7446 offset = extractLine(buffer, offset, str); 7447 int numSigs = Integer.parseInt(str[0]); 7448 if (numSigs > 0) { 7449 Signature[] sigs = new Signature[numSigs]; 7450 for (int i = 0; i < numSigs; i++) { 7451 offset = extractLine(buffer, offset, str); 7452 sigs[i] = new Signature(str[0]); 7453 } 7454 mManifestSignatures.put(info.packageName, sigs); 7455 7456 // Okay, got the manifest info we need... 7457 try { 7458 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 7459 info.packageName, PackageManager.GET_SIGNATURES); 7460 // Fall through to IGNORE if the app explicitly disallows backup 7461 final int flags = pkgInfo.applicationInfo.flags; 7462 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 7463 // Restore system-uid-space packages only if they have 7464 // defined a custom backup agent 7465 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 7466 || (pkgInfo.applicationInfo.backupAgentName != null)) { 7467 // Verify signatures against any installed version; if they 7468 // don't match, then we fall though and ignore the data. The 7469 // signatureMatch() method explicitly ignores the signature 7470 // check for packages installed on the system partition, because 7471 // such packages are signed with the platform cert instead of 7472 // the app developer's cert, so they're different on every 7473 // device. 7474 if (signaturesMatch(sigs, pkgInfo)) { 7475 if (pkgInfo.versionCode >= version) { 7476 Slog.i(TAG, "Sig + version match; taking data"); 7477 policy = RestorePolicy.ACCEPT; 7478 } else { 7479 // The data is from a newer version of the app than 7480 // is presently installed. That means we can only 7481 // use it if the matching apk is also supplied. 7482 Slog.d(TAG, "Data version " + version 7483 + " is newer than installed version " 7484 + pkgInfo.versionCode + " - requiring apk"); 7485 policy = RestorePolicy.ACCEPT_IF_APK; 7486 } 7487 } else { 7488 Slog.w(TAG, "Restore manifest signatures do not match " 7489 + "installed application for " + info.packageName); 7490 } 7491 } else { 7492 Slog.w(TAG, "Package " + info.packageName 7493 + " is system level with no agent"); 7494 } 7495 } else { 7496 if (DEBUG) Slog.i(TAG, "Restore manifest from " 7497 + info.packageName + " but allowBackup=false"); 7498 } 7499 } catch (NameNotFoundException e) { 7500 // Okay, the target app isn't installed. We can process 7501 // the restore properly only if the dataset provides the 7502 // apk file and we can successfully install it. 7503 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 7504 + " not installed; requiring apk in dataset"); 7505 policy = RestorePolicy.ACCEPT_IF_APK; 7506 } 7507 7508 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 7509 Slog.i(TAG, "Cannot restore package " + info.packageName 7510 + " without the matching .apk"); 7511 } 7512 } else { 7513 Slog.i(TAG, "Missing signature on backed-up package " 7514 + info.packageName); 7515 } 7516 } else { 7517 Slog.i(TAG, "Expected package " + info.packageName 7518 + " but restore manifest claims " + manifestPackage); 7519 } 7520 } else { 7521 Slog.i(TAG, "Unknown restore manifest version " + version 7522 + " for package " + info.packageName); 7523 } 7524 } catch (NumberFormatException e) { 7525 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 7526 } catch (IllegalArgumentException e) { 7527 Slog.w(TAG, e.getMessage()); 7528 } 7529 7530 return policy; 7531 } 7532 7533 // Builds a line from a byte buffer starting at 'offset', and returns 7534 // the index of the next unconsumed data in the buffer. 7535 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 7536 final int end = buffer.length; 7537 if (offset >= end) throw new IOException("Incomplete data"); 7538 7539 int pos; 7540 for (pos = offset; pos < end; pos++) { 7541 byte c = buffer[pos]; 7542 // at LF we declare end of line, and return the next char as the 7543 // starting point for the next time through 7544 if (c == '\n') { 7545 break; 7546 } 7547 } 7548 outStr[0] = new String(buffer, offset, pos - offset); 7549 pos++; // may be pointing an extra byte past the end but that's okay 7550 return pos; 7551 } 7552 7553 void dumpFileMetadata(FileMetadata info) { 7554 if (DEBUG) { 7555 StringBuilder b = new StringBuilder(128); 7556 7557 // mode string 7558 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 7559 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 7560 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 7561 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 7562 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 7563 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 7564 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 7565 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 7566 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 7567 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 7568 b.append(String.format(" %9d ", info.size)); 7569 7570 Date stamp = new Date(info.mtime); 7571 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 7572 7573 b.append(info.packageName); 7574 b.append(" :: "); 7575 b.append(info.domain); 7576 b.append(" :: "); 7577 b.append(info.path); 7578 7579 Slog.i(TAG, b.toString()); 7580 } 7581 } 7582 // Consume a tar file header block [sequence] and accumulate the relevant metadata 7583 FileMetadata readTarHeaders(InputStream instream) throws IOException { 7584 byte[] block = new byte[512]; 7585 FileMetadata info = null; 7586 7587 boolean gotHeader = readTarHeader(instream, block); 7588 if (gotHeader) { 7589 try { 7590 // okay, presume we're okay, and extract the various metadata 7591 info = new FileMetadata(); 7592 info.size = extractRadix(block, 124, 12, 8); 7593 info.mtime = extractRadix(block, 136, 12, 8); 7594 info.mode = extractRadix(block, 100, 8, 8); 7595 7596 info.path = extractString(block, 345, 155); // prefix 7597 String path = extractString(block, 0, 100); 7598 if (path.length() > 0) { 7599 if (info.path.length() > 0) info.path += '/'; 7600 info.path += path; 7601 } 7602 7603 // tar link indicator field: 1 byte at offset 156 in the header. 7604 int typeChar = block[156]; 7605 if (typeChar == 'x') { 7606 // pax extended header, so we need to read that 7607 gotHeader = readPaxExtendedHeader(instream, info); 7608 if (gotHeader) { 7609 // and after a pax extended header comes another real header -- read 7610 // that to find the real file type 7611 gotHeader = readTarHeader(instream, block); 7612 } 7613 if (!gotHeader) throw new IOException("Bad or missing pax header"); 7614 7615 typeChar = block[156]; 7616 } 7617 7618 switch (typeChar) { 7619 case '0': info.type = BackupAgent.TYPE_FILE; break; 7620 case '5': { 7621 info.type = BackupAgent.TYPE_DIRECTORY; 7622 if (info.size != 0) { 7623 Slog.w(TAG, "Directory entry with nonzero size in header"); 7624 info.size = 0; 7625 } 7626 break; 7627 } 7628 case 0: { 7629 // presume EOF 7630 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 7631 return null; 7632 } 7633 default: { 7634 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 7635 throw new IOException("Unknown entity type " + typeChar); 7636 } 7637 } 7638 7639 // Parse out the path 7640 // 7641 // first: apps/shared/unrecognized 7642 if (FullBackup.SHARED_PREFIX.regionMatches(0, 7643 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 7644 // File in shared storage. !!! TODO: implement this. 7645 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 7646 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 7647 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 7648 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 7649 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 7650 info.path, 0, FullBackup.APPS_PREFIX.length())) { 7651 // App content! Parse out the package name and domain 7652 7653 // strip the apps/ prefix 7654 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 7655 7656 // extract the package name 7657 int slash = info.path.indexOf('/'); 7658 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 7659 info.packageName = info.path.substring(0, slash); 7660 info.path = info.path.substring(slash+1); 7661 7662 // if it's a manifest or metadata payload we're done, otherwise parse 7663 // out the domain into which the file will be restored 7664 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 7665 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 7666 slash = info.path.indexOf('/'); 7667 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path); 7668 info.domain = info.path.substring(0, slash); 7669 info.path = info.path.substring(slash + 1); 7670 } 7671 } 7672 } catch (IOException e) { 7673 if (DEBUG) { 7674 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 7675 HEXLOG(block); 7676 } 7677 throw e; 7678 } 7679 } 7680 return info; 7681 } 7682 7683 private void HEXLOG(byte[] block) { 7684 int offset = 0; 7685 int todo = block.length; 7686 StringBuilder buf = new StringBuilder(64); 7687 while (todo > 0) { 7688 buf.append(String.format("%04x ", offset)); 7689 int numThisLine = (todo > 16) ? 16 : todo; 7690 for (int i = 0; i < numThisLine; i++) { 7691 buf.append(String.format("%02x ", block[offset+i])); 7692 } 7693 Slog.i("hexdump", buf.toString()); 7694 buf.setLength(0); 7695 todo -= numThisLine; 7696 offset += numThisLine; 7697 } 7698 } 7699 7700 // Read exactly the given number of bytes into a buffer at the stated offset. 7701 // Returns false if EOF is encountered before the requested number of bytes 7702 // could be read. 7703 int readExactly(InputStream in, byte[] buffer, int offset, int size) 7704 throws IOException { 7705 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 7706 7707 int soFar = 0; 7708 while (soFar < size) { 7709 int nRead = in.read(buffer, offset + soFar, size - soFar); 7710 if (nRead <= 0) { 7711 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 7712 break; 7713 } 7714 soFar += nRead; 7715 } 7716 return soFar; 7717 } 7718 7719 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 7720 final int got = readExactly(instream, block, 0, 512); 7721 if (got == 0) return false; // Clean EOF 7722 if (got < 512) throw new IOException("Unable to read full block header"); 7723 mBytes += 512; 7724 return true; 7725 } 7726 7727 // overwrites 'info' fields based on the pax extended header 7728 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 7729 throws IOException { 7730 // We should never see a pax extended header larger than this 7731 if (info.size > 32*1024) { 7732 Slog.w(TAG, "Suspiciously large pax header size " + info.size 7733 + " - aborting"); 7734 throw new IOException("Sanity failure: pax header size " + info.size); 7735 } 7736 7737 // read whole blocks, not just the content size 7738 int numBlocks = (int)((info.size + 511) >> 9); 7739 byte[] data = new byte[numBlocks * 512]; 7740 if (readExactly(instream, data, 0, data.length) < data.length) { 7741 throw new IOException("Unable to read full pax header"); 7742 } 7743 mBytes += data.length; 7744 7745 final int contentSize = (int) info.size; 7746 int offset = 0; 7747 do { 7748 // extract the line at 'offset' 7749 int eol = offset+1; 7750 while (eol < contentSize && data[eol] != ' ') eol++; 7751 if (eol >= contentSize) { 7752 // error: we just hit EOD looking for the end of the size field 7753 throw new IOException("Invalid pax data"); 7754 } 7755 // eol points to the space between the count and the key 7756 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 7757 int key = eol + 1; // start of key=value 7758 eol = offset + linelen - 1; // trailing LF 7759 int value; 7760 for (value = key+1; data[value] != '=' && value <= eol; value++); 7761 if (value > eol) { 7762 throw new IOException("Invalid pax declaration"); 7763 } 7764 7765 // pax requires that key/value strings be in UTF-8 7766 String keyStr = new String(data, key, value-key, "UTF-8"); 7767 // -1 to strip the trailing LF 7768 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 7769 7770 if ("path".equals(keyStr)) { 7771 info.path = valStr; 7772 } else if ("size".equals(keyStr)) { 7773 info.size = Long.parseLong(valStr); 7774 } else { 7775 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 7776 } 7777 7778 offset += linelen; 7779 } while (offset < contentSize); 7780 7781 return true; 7782 } 7783 7784 long extractRadix(byte[] data, int offset, int maxChars, int radix) 7785 throws IOException { 7786 long value = 0; 7787 final int end = offset + maxChars; 7788 for (int i = offset; i < end; i++) { 7789 final byte b = data[i]; 7790 // Numeric fields in tar can terminate with either NUL or SPC 7791 if (b == 0 || b == ' ') break; 7792 if (b < '0' || b > ('0' + radix - 1)) { 7793 throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix); 7794 } 7795 value = radix * value + (b - '0'); 7796 } 7797 return value; 7798 } 7799 7800 String extractString(byte[] data, int offset, int maxChars) throws IOException { 7801 final int end = offset + maxChars; 7802 int eos = offset; 7803 // tar string fields terminate early with a NUL 7804 while (eos < end && data[eos] != 0) eos++; 7805 return new String(data, offset, eos-offset, "US-ASCII"); 7806 } 7807 7808 void sendStartRestore() { 7809 if (mObserver != null) { 7810 try { 7811 mObserver.onStartRestore(); 7812 } catch (RemoteException e) { 7813 Slog.w(TAG, "full restore observer went away: startRestore"); 7814 mObserver = null; 7815 } 7816 } 7817 } 7818 7819 void sendOnRestorePackage(String name) { 7820 if (mObserver != null) { 7821 try { 7822 // TODO: use a more user-friendly name string 7823 mObserver.onRestorePackage(name); 7824 } catch (RemoteException e) { 7825 Slog.w(TAG, "full restore observer went away: restorePackage"); 7826 mObserver = null; 7827 } 7828 } 7829 } 7830 7831 void sendEndRestore() { 7832 if (mObserver != null) { 7833 try { 7834 mObserver.onEndRestore(); 7835 } catch (RemoteException e) { 7836 Slog.w(TAG, "full restore observer went away: endRestore"); 7837 mObserver = null; 7838 } 7839 } 7840 } 7841 } 7842 7843 // ----- Restore handling ----- 7844 7845 // Old style: directly match the stored vs on device signature blocks 7846 static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 7847 if (target == null) { 7848 return false; 7849 } 7850 7851 // If the target resides on the system partition, we allow it to restore 7852 // data from the like-named package in a restore set even if the signatures 7853 // do not match. (Unlike general applications, those flashed to the system 7854 // partition will be signed with the device's platform certificate, so on 7855 // different phones the same system app will have different signatures.) 7856 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 7857 if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 7858 return true; 7859 } 7860 7861 // Allow unsigned apps, but not signed on one device and unsigned on the other 7862 // !!! TODO: is this the right policy? 7863 Signature[] deviceSigs = target.signatures; 7864 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs 7865 + " device=" + deviceSigs); 7866 if ((storedSigs == null || storedSigs.length == 0) 7867 && (deviceSigs == null || deviceSigs.length == 0)) { 7868 return true; 7869 } 7870 if (storedSigs == null || deviceSigs == null) { 7871 return false; 7872 } 7873 7874 // !!! TODO: this demands that every stored signature match one 7875 // that is present on device, and does not demand the converse. 7876 // Is this this right policy? 7877 int nStored = storedSigs.length; 7878 int nDevice = deviceSigs.length; 7879 7880 for (int i=0; i < nStored; i++) { 7881 boolean match = false; 7882 for (int j=0; j < nDevice; j++) { 7883 if (storedSigs[i].equals(deviceSigs[j])) { 7884 match = true; 7885 break; 7886 } 7887 } 7888 if (!match) { 7889 return false; 7890 } 7891 } 7892 return true; 7893 } 7894 7895 // Used by both incremental and full restore 7896 void restoreWidgetData(String packageName, byte[] widgetData) { 7897 // Apply the restored widget state and generate the ID update for the app 7898 // TODO: http://b/22388012 7899 if (MORE_DEBUG) { 7900 Slog.i(TAG, "Incorporating restored widget data"); 7901 } 7902 AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM); 7903 } 7904 7905 // ***************************** 7906 // NEW UNIFIED RESTORE IMPLEMENTATION 7907 // ***************************** 7908 7909 // states of the unified-restore state machine 7910 enum UnifiedRestoreState { 7911 INITIAL, 7912 RUNNING_QUEUE, 7913 RESTORE_KEYVALUE, 7914 RESTORE_FULL, 7915 RESTORE_FINISHED, 7916 FINAL 7917 } 7918 7919 class PerformUnifiedRestoreTask implements BackupRestoreTask { 7920 // Transport we're working with to do the restore 7921 private IBackupTransport mTransport; 7922 7923 // Where per-transport saved state goes 7924 File mStateDir; 7925 7926 // Restore observer; may be null 7927 private IRestoreObserver mObserver; 7928 7929 // Token identifying the dataset to the transport 7930 private long mToken; 7931 7932 // When this is a restore-during-install, this is the token identifying the 7933 // operation to the Package Manager, and we must ensure that we let it know 7934 // when we're finished. 7935 private int mPmToken; 7936 7937 // When this is restore-during-install, we need to tell the package manager 7938 // whether we actually launched the app, because this affects notifications 7939 // around externally-visible state transitions. 7940 private boolean mDidLaunch; 7941 7942 // Is this a whole-system restore, i.e. are we establishing a new ancestral 7943 // dataset to base future restore-at-install operations from? 7944 private boolean mIsSystemRestore; 7945 7946 // If this is a single-package restore, what package are we interested in? 7947 private PackageInfo mTargetPackage; 7948 7949 // In all cases, the calculated list of packages that we are trying to restore 7950 private List<PackageInfo> mAcceptSet; 7951 7952 // Our bookkeeping about the ancestral dataset 7953 private PackageManagerBackupAgent mPmAgent; 7954 7955 // Currently-bound backup agent for restore + restoreFinished purposes 7956 private IBackupAgent mAgent; 7957 7958 // What sort of restore we're doing now 7959 private RestoreDescription mRestoreDescription; 7960 7961 // The package we're currently restoring 7962 private PackageInfo mCurrentPackage; 7963 7964 // Widget-related data handled as part of this restore operation 7965 private byte[] mWidgetData; 7966 7967 // Number of apps restored in this pass 7968 private int mCount; 7969 7970 // When did we start? 7971 private long mStartRealtime; 7972 7973 // State machine progress 7974 private UnifiedRestoreState mState; 7975 7976 // How are things going? 7977 private int mStatus; 7978 7979 // Done? 7980 private boolean mFinished; 7981 7982 // Key/value: bookkeeping about staged data and files for agent access 7983 private File mBackupDataName; 7984 private File mStageName; 7985 private File mSavedStateName; 7986 private File mNewStateName; 7987 ParcelFileDescriptor mBackupData; 7988 ParcelFileDescriptor mNewState; 7989 7990 // Invariant: mWakelock is already held, and this task is responsible for 7991 // releasing it at the end of the restore operation. 7992 PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer, 7993 long restoreSetToken, PackageInfo targetPackage, int pmToken, 7994 boolean isFullSystemRestore, String[] filterSet) { 7995 mState = UnifiedRestoreState.INITIAL; 7996 mStartRealtime = SystemClock.elapsedRealtime(); 7997 7998 mTransport = transport; 7999 mObserver = observer; 8000 mToken = restoreSetToken; 8001 mPmToken = pmToken; 8002 mTargetPackage = targetPackage; 8003 mIsSystemRestore = isFullSystemRestore; 8004 mFinished = false; 8005 mDidLaunch = false; 8006 8007 if (targetPackage != null) { 8008 // Single package restore 8009 mAcceptSet = new ArrayList<PackageInfo>(); 8010 mAcceptSet.add(targetPackage); 8011 } else { 8012 // Everything possible, or a target set 8013 if (filterSet == null) { 8014 // We want everything and a pony 8015 List<PackageInfo> apps = 8016 PackageManagerBackupAgent.getStorableApplications(mPackageManager); 8017 filterSet = packagesToNames(apps); 8018 if (DEBUG) { 8019 Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps"); 8020 } 8021 } 8022 8023 mAcceptSet = new ArrayList<PackageInfo>(filterSet.length); 8024 8025 // Pro tem, we insist on moving the settings provider package to last place. 8026 // Keep track of whether it's in the list, and bump it down if so. We also 8027 // want to do the system package itself first if it's called for. 8028 boolean hasSystem = false; 8029 boolean hasSettings = false; 8030 for (int i = 0; i < filterSet.length; i++) { 8031 try { 8032 PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0); 8033 if ("android".equals(info.packageName)) { 8034 hasSystem = true; 8035 continue; 8036 } 8037 if (SETTINGS_PACKAGE.equals(info.packageName)) { 8038 hasSettings = true; 8039 continue; 8040 } 8041 8042 if (appIsEligibleForBackup(info.applicationInfo)) { 8043 mAcceptSet.add(info); 8044 } 8045 } catch (NameNotFoundException e) { 8046 // requested package name doesn't exist; ignore it 8047 } 8048 } 8049 if (hasSystem) { 8050 try { 8051 mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0)); 8052 } catch (NameNotFoundException e) { 8053 // won't happen; we know a priori that it's valid 8054 } 8055 } 8056 if (hasSettings) { 8057 try { 8058 mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0)); 8059 } catch (NameNotFoundException e) { 8060 // this one is always valid too 8061 } 8062 } 8063 } 8064 8065 if (MORE_DEBUG) { 8066 Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size()); 8067 for (PackageInfo info : mAcceptSet) { 8068 Slog.v(TAG, " " + info.packageName); 8069 } 8070 } 8071 } 8072 8073 private String[] packagesToNames(List<PackageInfo> apps) { 8074 final int N = apps.size(); 8075 String[] names = new String[N]; 8076 for (int i = 0; i < N; i++) { 8077 names[i] = apps.get(i).packageName; 8078 } 8079 return names; 8080 } 8081 8082 // Execute one tick of whatever state machine the task implements 8083 @Override 8084 public void execute() { 8085 if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState); 8086 switch (mState) { 8087 case INITIAL: 8088 startRestore(); 8089 break; 8090 8091 case RUNNING_QUEUE: 8092 dispatchNextRestore(); 8093 break; 8094 8095 case RESTORE_KEYVALUE: 8096 restoreKeyValue(); 8097 break; 8098 8099 case RESTORE_FULL: 8100 restoreFull(); 8101 break; 8102 8103 case RESTORE_FINISHED: 8104 restoreFinished(); 8105 break; 8106 8107 case FINAL: 8108 if (!mFinished) finalizeRestore(); 8109 else { 8110 Slog.e(TAG, "Duplicate finish"); 8111 } 8112 mFinished = true; 8113 break; 8114 } 8115 } 8116 8117 /* 8118 * SKETCH OF OPERATION 8119 * 8120 * create one of these PerformUnifiedRestoreTask objects, telling it which 8121 * dataset & transport to address, and then parameters within the restore 8122 * operation: single target package vs many, etc. 8123 * 8124 * 1. transport.startRestore(token, list-of-packages). If we need @pm@ it is 8125 * always placed first and the settings provider always placed last [for now]. 8126 * 8127 * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline] 8128 * 8129 * [ state change => RUNNING_QUEUE ] 8130 * 8131 * NOW ITERATE: 8132 * 8133 * { 3. t.nextRestorePackage() 8134 * 4. does the metadata for this package allow us to restore it? 8135 * does the on-disk app permit us to restore it? [re-check allowBackup etc] 8136 * 5. is this a key/value dataset? => key/value agent restore 8137 * [ state change => RESTORE_KEYVALUE ] 8138 * 5a. spin up agent 8139 * 5b. t.getRestoreData() to stage it properly 8140 * 5c. call into agent to perform restore 8141 * 5d. tear down agent 8142 * [ state change => RUNNING_QUEUE ] 8143 * 8144 * 6. else it's a stream dataset: 8145 * [ state change => RESTORE_FULL ] 8146 * 6a. instantiate the engine for a stream restore: engine handles agent lifecycles 8147 * 6b. spin off engine runner on separate thread 8148 * 6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket 8149 * [ state change => RUNNING_QUEUE ] 8150 * } 8151 * 8152 * [ state change => FINAL ] 8153 * 8154 * 7. t.finishRestore(), release wakelock, etc. 8155 * 8156 * 8157 */ 8158 8159 // state INITIAL : set up for the restore and read the metadata if necessary 8160 private void startRestore() { 8161 sendStartRestore(mAcceptSet.size()); 8162 8163 // If we're starting a full-system restore, set up to begin widget ID remapping 8164 if (mIsSystemRestore) { 8165 // TODO: http://b/22388012 8166 AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM); 8167 } 8168 8169 try { 8170 String transportDir = mTransport.transportDirName(); 8171 mStateDir = new File(mBaseStateDir, transportDir); 8172 8173 // Fetch the current metadata from the dataset first 8174 PackageInfo pmPackage = new PackageInfo(); 8175 pmPackage.packageName = PACKAGE_MANAGER_SENTINEL; 8176 mAcceptSet.add(0, pmPackage); 8177 8178 PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]); 8179 mStatus = mTransport.startRestore(mToken, packages); 8180 if (mStatus != BackupTransport.TRANSPORT_OK) { 8181 Slog.e(TAG, "Transport error " + mStatus + "; no restore possible"); 8182 mStatus = BackupTransport.TRANSPORT_ERROR; 8183 executeNextState(UnifiedRestoreState.FINAL); 8184 return; 8185 } 8186 8187 RestoreDescription desc = mTransport.nextRestorePackage(); 8188 if (desc == null) { 8189 Slog.e(TAG, "No restore metadata available; halting"); 8190 mStatus = BackupTransport.TRANSPORT_ERROR; 8191 executeNextState(UnifiedRestoreState.FINAL); 8192 return; 8193 } 8194 if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) { 8195 Slog.e(TAG, "Required metadata but got " + desc.getPackageName()); 8196 mStatus = BackupTransport.TRANSPORT_ERROR; 8197 executeNextState(UnifiedRestoreState.FINAL); 8198 return; 8199 } 8200 8201 // Pull the Package Manager metadata from the restore set first 8202 mCurrentPackage = new PackageInfo(); 8203 mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL; 8204 mPmAgent = new PackageManagerBackupAgent(mPackageManager, null); 8205 mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind()); 8206 if (MORE_DEBUG) { 8207 Slog.v(TAG, "initiating restore for PMBA"); 8208 } 8209 initiateOneRestore(mCurrentPackage, 0); 8210 // The PM agent called operationComplete() already, because our invocation 8211 // of it is process-local and therefore synchronous. That means that the 8212 // next-state message (RUNNING_QUEUE) is already enqueued. Only if we're 8213 // unable to proceed with running the queue do we remove that pending 8214 // message and jump straight to the FINAL state. Because this was 8215 // synchronous we also know that we should cancel the pending timeout 8216 // message. 8217 mBackupHandler.removeMessages(MSG_TIMEOUT); 8218 8219 // Verify that the backup set includes metadata. If not, we can't do 8220 // signature/version verification etc, so we simply do not proceed with 8221 // the restore operation. 8222 if (!mPmAgent.hasMetadata()) { 8223 Slog.e(TAG, "No restore metadata available, so not restoring"); 8224 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8225 PACKAGE_MANAGER_SENTINEL, 8226 "Package manager restore metadata missing"); 8227 mStatus = BackupTransport.TRANSPORT_ERROR; 8228 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 8229 executeNextState(UnifiedRestoreState.FINAL); 8230 return; 8231 } 8232 8233 // Success; cache the metadata and continue as expected with the 8234 // next state already enqueued 8235 8236 } catch (RemoteException e) { 8237 // If we lost the transport at any time, halt 8238 Slog.e(TAG, "Unable to contact transport for restore"); 8239 mStatus = BackupTransport.TRANSPORT_ERROR; 8240 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 8241 executeNextState(UnifiedRestoreState.FINAL); 8242 return; 8243 } 8244 } 8245 8246 // state RUNNING_QUEUE : figure out what the next thing to be restored is, 8247 // and fire the appropriate next step 8248 private void dispatchNextRestore() { 8249 UnifiedRestoreState nextState = UnifiedRestoreState.FINAL; 8250 try { 8251 mRestoreDescription = mTransport.nextRestorePackage(); 8252 final String pkgName = (mRestoreDescription != null) 8253 ? mRestoreDescription.getPackageName() : null; 8254 if (pkgName == null) { 8255 Slog.e(TAG, "Failure getting next package name"); 8256 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8257 nextState = UnifiedRestoreState.FINAL; 8258 return; 8259 } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) { 8260 // Yay we've reached the end cleanly 8261 if (DEBUG) { 8262 Slog.v(TAG, "No more packages; finishing restore"); 8263 } 8264 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); 8265 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis); 8266 nextState = UnifiedRestoreState.FINAL; 8267 return; 8268 } 8269 8270 if (DEBUG) { 8271 Slog.i(TAG, "Next restore package: " + mRestoreDescription); 8272 } 8273 sendOnRestorePackage(pkgName); 8274 8275 Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName); 8276 if (metaInfo == null) { 8277 Slog.e(TAG, "No metadata for " + pkgName); 8278 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 8279 "Package metadata missing"); 8280 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8281 return; 8282 } 8283 8284 try { 8285 mCurrentPackage = mPackageManager.getPackageInfo( 8286 pkgName, PackageManager.GET_SIGNATURES); 8287 } catch (NameNotFoundException e) { 8288 // Whoops, we thought we could restore this package but it 8289 // turns out not to be present. Skip it. 8290 Slog.e(TAG, "Package not present: " + pkgName); 8291 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 8292 "Package missing on device"); 8293 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8294 return; 8295 } 8296 8297 if (metaInfo.versionCode > mCurrentPackage.versionCode) { 8298 // Data is from a "newer" version of the app than we have currently 8299 // installed. If the app has not declared that it is prepared to 8300 // handle this case, we do not attempt the restore. 8301 if ((mCurrentPackage.applicationInfo.flags 8302 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { 8303 String message = "Version " + metaInfo.versionCode 8304 + " > installed version " + mCurrentPackage.versionCode; 8305 Slog.w(TAG, "Package " + pkgName + ": " + message); 8306 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8307 pkgName, message); 8308 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8309 return; 8310 } else { 8311 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode 8312 + " > installed " + mCurrentPackage.versionCode 8313 + " but restoreAnyVersion"); 8314 } 8315 } 8316 8317 if (MORE_DEBUG) Slog.v(TAG, "Package " + pkgName 8318 + " restore version [" + metaInfo.versionCode 8319 + "] is compatible with installed version [" 8320 + mCurrentPackage.versionCode + "]"); 8321 8322 // Reset per-package preconditions and fire the appropriate next state 8323 mWidgetData = null; 8324 final int type = mRestoreDescription.getDataType(); 8325 if (type == RestoreDescription.TYPE_KEY_VALUE) { 8326 nextState = UnifiedRestoreState.RESTORE_KEYVALUE; 8327 } else if (type == RestoreDescription.TYPE_FULL_STREAM) { 8328 nextState = UnifiedRestoreState.RESTORE_FULL; 8329 } else { 8330 // Unknown restore type; ignore this package and move on 8331 Slog.e(TAG, "Unrecognized restore type " + type); 8332 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8333 return; 8334 } 8335 } catch (RemoteException e) { 8336 Slog.e(TAG, "Can't get next target from transport; ending restore"); 8337 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8338 nextState = UnifiedRestoreState.FINAL; 8339 return; 8340 } finally { 8341 executeNextState(nextState); 8342 } 8343 } 8344 8345 // state RESTORE_KEYVALUE : restore one package via key/value API set 8346 private void restoreKeyValue() { 8347 // Initiating the restore will pass responsibility for the state machine's 8348 // progress to the agent callback, so we do not always execute the 8349 // next state here. 8350 final String packageName = mCurrentPackage.packageName; 8351 // Validate some semantic requirements that apply in this way 8352 // only to the key/value restore API flow 8353 if (mCurrentPackage.applicationInfo.backupAgentName == null 8354 || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) { 8355 if (MORE_DEBUG) { 8356 Slog.i(TAG, "Data exists for package " + packageName 8357 + " but app has no agent; skipping"); 8358 } 8359 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8360 "Package has no agent"); 8361 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8362 return; 8363 } 8364 8365 Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); 8366 if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { 8367 Slog.w(TAG, "Signature mismatch restoring " + packageName); 8368 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8369 "Signature mismatch"); 8370 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8371 return; 8372 } 8373 8374 // Good to go! Set up and bind the agent... 8375 mAgent = bindToAgentSynchronous( 8376 mCurrentPackage.applicationInfo, 8377 IApplicationThread.BACKUP_MODE_INCREMENTAL); 8378 if (mAgent == null) { 8379 Slog.w(TAG, "Can't find backup agent for " + packageName); 8380 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8381 "Restore agent missing"); 8382 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8383 return; 8384 } 8385 8386 // Whatever happens next, we've launched the target app now; remember that. 8387 mDidLaunch = true; 8388 8389 // And then finally start the restore on this agent 8390 try { 8391 initiateOneRestore(mCurrentPackage, metaInfo.versionCode); 8392 ++mCount; 8393 } catch (Exception e) { 8394 Slog.e(TAG, "Error when attempting restore: " + e.toString()); 8395 keyValueAgentErrorCleanup(); 8396 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8397 } 8398 } 8399 8400 // Guts of a key/value restore operation 8401 void initiateOneRestore(PackageInfo app, int appVersionCode) { 8402 final String packageName = app.packageName; 8403 8404 if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName); 8405 8406 // !!! TODO: get the dirs from the transport 8407 mBackupDataName = new File(mDataDir, packageName + ".restore"); 8408 mStageName = new File(mDataDir, packageName + ".stage"); 8409 mNewStateName = new File(mStateDir, packageName + ".new"); 8410 mSavedStateName = new File(mStateDir, packageName); 8411 8412 // don't stage the 'android' package where the wallpaper data lives. this is 8413 // an optimization: we know there's no widget data hosted/published by that 8414 // package, and this way we avoid doing a spurious copy of MB-sized wallpaper 8415 // data following the download. 8416 boolean staging = !packageName.equals("android"); 8417 ParcelFileDescriptor stage; 8418 File downloadFile = (staging) ? mStageName : mBackupDataName; 8419 8420 final int token = generateToken(); 8421 try { 8422 // Run the transport's restore pass 8423 stage = ParcelFileDescriptor.open(downloadFile, 8424 ParcelFileDescriptor.MODE_READ_WRITE | 8425 ParcelFileDescriptor.MODE_CREATE | 8426 ParcelFileDescriptor.MODE_TRUNCATE); 8427 8428 if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) { 8429 // Transport-level failure, so we wind everything up and 8430 // terminate the restore operation. 8431 Slog.e(TAG, "Error getting restore data for " + packageName); 8432 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8433 stage.close(); 8434 downloadFile.delete(); 8435 executeNextState(UnifiedRestoreState.FINAL); 8436 return; 8437 } 8438 8439 // We have the data from the transport. Now we extract and strip 8440 // any per-package metadata (typically widget-related information) 8441 // if appropriate 8442 if (staging) { 8443 stage.close(); 8444 stage = ParcelFileDescriptor.open(downloadFile, 8445 ParcelFileDescriptor.MODE_READ_ONLY); 8446 8447 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 8448 ParcelFileDescriptor.MODE_READ_WRITE | 8449 ParcelFileDescriptor.MODE_CREATE | 8450 ParcelFileDescriptor.MODE_TRUNCATE); 8451 8452 BackupDataInput in = new BackupDataInput(stage.getFileDescriptor()); 8453 BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor()); 8454 byte[] buffer = new byte[8192]; // will grow when needed 8455 while (in.readNextHeader()) { 8456 final String key = in.getKey(); 8457 final int size = in.getDataSize(); 8458 8459 // is this a special key? 8460 if (key.equals(KEY_WIDGET_STATE)) { 8461 if (DEBUG) { 8462 Slog.i(TAG, "Restoring widget state for " + packageName); 8463 } 8464 mWidgetData = new byte[size]; 8465 in.readEntityData(mWidgetData, 0, size); 8466 } else { 8467 if (size > buffer.length) { 8468 buffer = new byte[size]; 8469 } 8470 in.readEntityData(buffer, 0, size); 8471 out.writeEntityHeader(key, size); 8472 out.writeEntityData(buffer, size); 8473 } 8474 } 8475 8476 mBackupData.close(); 8477 } 8478 8479 // Okay, we have the data. Now have the agent do the restore. 8480 stage.close(); 8481 8482 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 8483 ParcelFileDescriptor.MODE_READ_ONLY); 8484 8485 mNewState = ParcelFileDescriptor.open(mNewStateName, 8486 ParcelFileDescriptor.MODE_READ_WRITE | 8487 ParcelFileDescriptor.MODE_CREATE | 8488 ParcelFileDescriptor.MODE_TRUNCATE); 8489 8490 // Kick off the restore, checking for hung agents. The timeout or 8491 // the operationComplete() callback will schedule the next step, 8492 // so we do not do that here. 8493 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this); 8494 mAgent.doRestore(mBackupData, appVersionCode, mNewState, 8495 token, mBackupManagerBinder); 8496 } catch (Exception e) { 8497 Slog.e(TAG, "Unable to call app for restore: " + packageName, e); 8498 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8499 packageName, e.toString()); 8500 keyValueAgentErrorCleanup(); // clears any pending timeout messages as well 8501 8502 // After a restore failure we go back to running the queue. If there 8503 // are no more packages to be restored that will be handled by the 8504 // next step. 8505 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8506 } 8507 } 8508 8509 // state RESTORE_FULL : restore one package via streaming engine 8510 private void restoreFull() { 8511 // None of this can run on the work looper here, so we spin asynchronous 8512 // work like this: 8513 // 8514 // StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk() 8515 // write it into the pipe to the engine 8516 // EngineThread: FullRestoreEngine thread communicating with the target app 8517 // 8518 // When finished, StreamFeederThread executes next state as appropriate on the 8519 // backup looper, and the overall unified restore task resumes 8520 try { 8521 StreamFeederThread feeder = new StreamFeederThread(); 8522 if (MORE_DEBUG) { 8523 Slog.i(TAG, "Spinning threads for stream restore of " 8524 + mCurrentPackage.packageName); 8525 } 8526 new Thread(feeder, "unified-stream-feeder").start(); 8527 8528 // At this point the feeder is responsible for advancing the restore 8529 // state, so we're done here. 8530 } catch (IOException e) { 8531 // Unable to instantiate the feeder thread -- we need to bail on the 8532 // current target. We haven't asked the transport for data yet, though, 8533 // so we can do that simply by going back to running the restore queue. 8534 Slog.e(TAG, "Unable to construct pipes for stream restore!"); 8535 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8536 } 8537 } 8538 8539 // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end 8540 private void restoreFinished() { 8541 try { 8542 final int token = generateToken(); 8543 prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this); 8544 mAgent.doRestoreFinished(token, mBackupManagerBinder); 8545 // If we get this far, the callback or timeout will schedule the 8546 // next restore state, so we're done 8547 } catch (Exception e) { 8548 final String packageName = mCurrentPackage.packageName; 8549 Slog.e(TAG, "Unable to finalize restore of " + packageName); 8550 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8551 packageName, e.toString()); 8552 keyValueAgentErrorCleanup(); 8553 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8554 } 8555 } 8556 8557 class StreamFeederThread extends RestoreEngine implements Runnable, BackupRestoreTask { 8558 final String TAG = "StreamFeederThread"; 8559 FullRestoreEngine mEngine; 8560 EngineThread mEngineThread; 8561 8562 // pipe through which we read data from the transport. [0] read, [1] write 8563 ParcelFileDescriptor[] mTransportPipes; 8564 8565 // pipe through which the engine will read data. [0] read, [1] write 8566 ParcelFileDescriptor[] mEnginePipes; 8567 8568 public StreamFeederThread() throws IOException { 8569 mTransportPipes = ParcelFileDescriptor.createPipe(); 8570 mEnginePipes = ParcelFileDescriptor.createPipe(); 8571 setRunning(true); 8572 } 8573 8574 @Override 8575 public void run() { 8576 UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE; 8577 int status = BackupTransport.TRANSPORT_OK; 8578 8579 EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE, 8580 mCurrentPackage.packageName); 8581 8582 mEngine = new FullRestoreEngine(this, null, mCurrentPackage, false, false); 8583 mEngineThread = new EngineThread(mEngine, mEnginePipes[0]); 8584 8585 ParcelFileDescriptor eWriteEnd = mEnginePipes[1]; 8586 ParcelFileDescriptor tReadEnd = mTransportPipes[0]; 8587 ParcelFileDescriptor tWriteEnd = mTransportPipes[1]; 8588 8589 int bufferSize = 32 * 1024; 8590 byte[] buffer = new byte[bufferSize]; 8591 FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor()); 8592 FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor()); 8593 8594 // spin up the engine and start moving data to it 8595 new Thread(mEngineThread, "unified-restore-engine").start(); 8596 8597 try { 8598 while (status == BackupTransport.TRANSPORT_OK) { 8599 // have the transport write some of the restoring data to us 8600 int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd); 8601 if (result > 0) { 8602 // The transport wrote this many bytes of restore data to the 8603 // pipe, so pass it along to the engine. 8604 if (MORE_DEBUG) { 8605 Slog.v(TAG, " <- transport provided chunk size " + result); 8606 } 8607 if (result > bufferSize) { 8608 bufferSize = result; 8609 buffer = new byte[bufferSize]; 8610 } 8611 int toCopy = result; 8612 while (toCopy > 0) { 8613 int n = transportIn.read(buffer, 0, toCopy); 8614 engineOut.write(buffer, 0, n); 8615 toCopy -= n; 8616 if (MORE_DEBUG) { 8617 Slog.v(TAG, " -> wrote " + n + " to engine, left=" + toCopy); 8618 } 8619 } 8620 } else if (result == BackupTransport.NO_MORE_DATA) { 8621 // Clean finish. Wind up and we're done! 8622 if (MORE_DEBUG) { 8623 Slog.i(TAG, "Got clean full-restore EOF for " 8624 + mCurrentPackage.packageName); 8625 } 8626 status = BackupTransport.TRANSPORT_OK; 8627 break; 8628 } else { 8629 // Transport reported some sort of failure; the fall-through 8630 // handling will deal properly with that. 8631 Slog.e(TAG, "Error " + result + " streaming restore for " 8632 + mCurrentPackage.packageName); 8633 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8634 status = result; 8635 } 8636 } 8637 if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through"); 8638 } catch (IOException e) { 8639 // We lost our ability to communicate via the pipes. That's worrying 8640 // but potentially recoverable; abandon this package's restore but 8641 // carry on with the next restore target. 8642 Slog.e(TAG, "Unable to route data for restore"); 8643 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8644 mCurrentPackage.packageName, "I/O error on pipes"); 8645 status = BackupTransport.AGENT_ERROR; 8646 } catch (RemoteException e) { 8647 // The transport went away; terminate the whole operation. Closing 8648 // the sockets will wake up the engine and it will then tidy up the 8649 // remote end. 8650 Slog.e(TAG, "Transport failed during restore"); 8651 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8652 status = BackupTransport.TRANSPORT_ERROR; 8653 } finally { 8654 // Close the transport pipes and *our* end of the engine pipe, 8655 // but leave the engine thread's end open so that it properly 8656 // hits EOF and winds up its operations. 8657 IoUtils.closeQuietly(mEnginePipes[1]); 8658 IoUtils.closeQuietly(mTransportPipes[0]); 8659 IoUtils.closeQuietly(mTransportPipes[1]); 8660 8661 // Don't proceed until the engine has wound up operations 8662 mEngineThread.waitForResult(); 8663 8664 // Now we're really done with this one too 8665 IoUtils.closeQuietly(mEnginePipes[0]); 8666 8667 // In all cases we want to remember whether we launched 8668 // the target app as part of our work so far. 8669 mDidLaunch = (mEngine.getAgent() != null); 8670 8671 // If we hit a transport-level error, we are done with everything; 8672 // if we hit an agent error we just go back to running the queue. 8673 if (status == BackupTransport.TRANSPORT_OK) { 8674 // Clean finish means we issue the restore-finished callback 8675 nextState = UnifiedRestoreState.RESTORE_FINISHED; 8676 8677 // the engine bound the target's agent, so recover that binding 8678 // to use for the callback. 8679 mAgent = mEngine.getAgent(); 8680 8681 // and the restored widget data, if any 8682 mWidgetData = mEngine.getWidgetData(); 8683 } else { 8684 // Something went wrong somewhere. Whether it was at the transport 8685 // level is immaterial; we need to tell the transport to bail 8686 try { 8687 mTransport.abortFullRestore(); 8688 } catch (RemoteException e) { 8689 // transport itself is dead; make sure we handle this as a 8690 // fatal error 8691 status = BackupTransport.TRANSPORT_ERROR; 8692 } 8693 8694 // We also need to wipe the current target's data, as it's probably 8695 // in an incoherent state. 8696 clearApplicationDataSynchronous(mCurrentPackage.packageName); 8697 8698 // Schedule the next state based on the nature of our failure 8699 if (status == BackupTransport.TRANSPORT_ERROR) { 8700 nextState = UnifiedRestoreState.FINAL; 8701 } else { 8702 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8703 } 8704 } 8705 executeNextState(nextState); 8706 setRunning(false); 8707 } 8708 } 8709 8710 // BackupRestoreTask interface, specifically for timeout handling 8711 8712 @Override 8713 public void execute() { /* intentionally empty */ } 8714 8715 @Override 8716 public void operationComplete(long result) { /* intentionally empty */ } 8717 8718 // The app has timed out handling a restoring file 8719 @Override 8720 public void handleTimeout() { 8721 if (DEBUG) { 8722 Slog.w(TAG, "Full-data restore target timed out; shutting down"); 8723 } 8724 mEngineThread.handleTimeout(); 8725 8726 IoUtils.closeQuietly(mEnginePipes[1]); 8727 mEnginePipes[1] = null; 8728 IoUtils.closeQuietly(mEnginePipes[0]); 8729 mEnginePipes[0] = null; 8730 } 8731 } 8732 8733 class EngineThread implements Runnable { 8734 FullRestoreEngine mEngine; 8735 FileInputStream mEngineStream; 8736 8737 EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) { 8738 mEngine = engine; 8739 engine.setRunning(true); 8740 // We *do* want this FileInputStream to own the underlying fd, so that 8741 // when we are finished with it, it closes this end of the pipe in a way 8742 // that signals its other end. 8743 mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true); 8744 } 8745 8746 public boolean isRunning() { 8747 return mEngine.isRunning(); 8748 } 8749 8750 public int waitForResult() { 8751 return mEngine.waitForResult(); 8752 } 8753 8754 @Override 8755 public void run() { 8756 try { 8757 while (mEngine.isRunning()) { 8758 // Tell it to be sure to leave the agent instance up after finishing 8759 mEngine.restoreOneFile(mEngineStream, false); 8760 } 8761 } finally { 8762 // Because mEngineStream adopted its underlying FD, this also 8763 // closes this end of the pipe. 8764 IoUtils.closeQuietly(mEngineStream); 8765 } 8766 } 8767 8768 public void handleTimeout() { 8769 IoUtils.closeQuietly(mEngineStream); 8770 mEngine.handleTimeout(); 8771 } 8772 } 8773 8774 // state FINAL : tear everything down and we're done. 8775 private void finalizeRestore() { 8776 if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver); 8777 8778 try { 8779 mTransport.finishRestore(); 8780 } catch (Exception e) { 8781 Slog.e(TAG, "Error finishing restore", e); 8782 } 8783 8784 // Tell the observer we're done 8785 if (mObserver != null) { 8786 try { 8787 mObserver.restoreFinished(mStatus); 8788 } catch (RemoteException e) { 8789 Slog.d(TAG, "Restore observer died at restoreFinished"); 8790 } 8791 } 8792 8793 // Clear any ongoing session timeout. 8794 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 8795 8796 // If we have a PM token, we must under all circumstances be sure to 8797 // handshake when we've finished. 8798 if (mPmToken > 0) { 8799 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken); 8800 try { 8801 mPackageManagerBinder.finishPackageInstall(mPmToken, mDidLaunch); 8802 } catch (RemoteException e) { /* can't happen */ } 8803 } else { 8804 // We were invoked via an active restore session, not by the Package 8805 // Manager, so start up the session timeout again. 8806 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, 8807 TIMEOUT_RESTORE_INTERVAL); 8808 } 8809 8810 // Kick off any work that may be needed regarding app widget restores 8811 // TODO: http://b/22388012 8812 AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM); 8813 8814 // If this was a full-system restore, record the ancestral 8815 // dataset information 8816 if (mIsSystemRestore && mPmAgent != null) { 8817 mAncestralPackages = mPmAgent.getRestoredPackages(); 8818 mAncestralToken = mToken; 8819 writeRestoreTokens(); 8820 } 8821 8822 // done; we can finally release the wakelock and be legitimately done. 8823 Slog.i(TAG, "Restore complete."); 8824 mWakelock.release(); 8825 } 8826 8827 void keyValueAgentErrorCleanup() { 8828 // If the agent fails restore, it might have put the app's data 8829 // into an incoherent state. For consistency we wipe its data 8830 // again in this case before continuing with normal teardown 8831 clearApplicationDataSynchronous(mCurrentPackage.packageName); 8832 keyValueAgentCleanup(); 8833 } 8834 8835 // TODO: clean up naming; this is now used at finish by both k/v and stream restores 8836 void keyValueAgentCleanup() { 8837 mBackupDataName.delete(); 8838 mStageName.delete(); 8839 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 8840 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 8841 mBackupData = mNewState = null; 8842 8843 // if everything went okay, remember the recorded state now 8844 // 8845 // !!! TODO: the restored data could be migrated on the server 8846 // side into the current dataset. In that case the new state file 8847 // we just created would reflect the data already extant in the 8848 // backend, so there'd be nothing more to do. Until that happens, 8849 // however, we need to make sure that we record the data to the 8850 // current backend dataset. (Yes, this means shipping the data over 8851 // the wire in both directions. That's bad, but consistency comes 8852 // first, then efficiency.) Once we introduce server-side data 8853 // migration to the newly-restored device's dataset, we will change 8854 // the following from a discard of the newly-written state to the 8855 // "correct" operation of renaming into the canonical state blob. 8856 mNewStateName.delete(); // TODO: remove; see above comment 8857 //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this 8858 8859 // If this wasn't the PM pseudopackage, tear down the agent side 8860 if (mCurrentPackage.applicationInfo != null) { 8861 // unbind and tidy up even on timeout or failure 8862 try { 8863 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 8864 8865 // The agent was probably running with a stub Application object, 8866 // which isn't a valid run mode for the main app logic. Shut 8867 // down the app so that next time it's launched, it gets the 8868 // usual full initialization. Note that this is only done for 8869 // full-system restores: when a single app has requested a restore, 8870 // it is explicitly not killed following that operation. 8871 // 8872 // We execute this kill when these conditions hold: 8873 // 1. it's not a system-uid process, 8874 // 2. the app did not request its own restore (mTargetPackage == null), and either 8875 // 3a. the app is a full-data target (TYPE_FULL_STREAM) or 8876 // b. the app does not state android:killAfterRestore="false" in its manifest 8877 final int appFlags = mCurrentPackage.applicationInfo.flags; 8878 final boolean killAfterRestore = 8879 (mCurrentPackage.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 8880 && ((mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM) 8881 || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0)); 8882 8883 if (mTargetPackage == null && killAfterRestore) { 8884 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of " 8885 + mCurrentPackage.applicationInfo.processName); 8886 mActivityManager.killApplicationProcess( 8887 mCurrentPackage.applicationInfo.processName, 8888 mCurrentPackage.applicationInfo.uid); 8889 } 8890 } catch (RemoteException e) { 8891 // can't happen; we run in the same process as the activity manager 8892 } 8893 } 8894 8895 // The caller is responsible for reestablishing the state machine; our 8896 // responsibility here is to clear the decks for whatever comes next. 8897 mBackupHandler.removeMessages(MSG_TIMEOUT, this); 8898 synchronized (mCurrentOpLock) { 8899 mCurrentOperations.clear(); 8900 } 8901 } 8902 8903 @Override 8904 public void operationComplete(long unusedResult) { 8905 if (MORE_DEBUG) { 8906 Slog.i(TAG, "operationComplete() during restore: target=" 8907 + mCurrentPackage.packageName 8908 + " state=" + mState); 8909 } 8910 8911 final UnifiedRestoreState nextState; 8912 switch (mState) { 8913 case INITIAL: 8914 // We've just (manually) restored the PMBA. It doesn't need the 8915 // additional restore-finished callback so we bypass that and go 8916 // directly to running the queue. 8917 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8918 break; 8919 8920 case RESTORE_KEYVALUE: 8921 case RESTORE_FULL: { 8922 // Okay, we've just heard back from the agent that it's done with 8923 // the restore itself. We now have to send the same agent its 8924 // doRestoreFinished() callback, so roll into that state. 8925 nextState = UnifiedRestoreState.RESTORE_FINISHED; 8926 break; 8927 } 8928 8929 case RESTORE_FINISHED: { 8930 // Okay, we're done with this package. Tidy up and go on to the next 8931 // app in the queue. 8932 int size = (int) mBackupDataName.length(); 8933 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, 8934 mCurrentPackage.packageName, size); 8935 8936 // Just go back to running the restore queue 8937 keyValueAgentCleanup(); 8938 8939 // If there was widget state associated with this app, get the OS to 8940 // incorporate it into current bookeeping and then pass that along to 8941 // the app as part of the restore-time work. 8942 if (mWidgetData != null) { 8943 restoreWidgetData(mCurrentPackage.packageName, mWidgetData); 8944 } 8945 8946 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8947 break; 8948 } 8949 8950 default: { 8951 // Some kind of horrible semantic error; we're in an unexpected state. 8952 // Back off hard and wind up. 8953 Slog.e(TAG, "Unexpected restore callback into state " + mState); 8954 keyValueAgentErrorCleanup(); 8955 nextState = UnifiedRestoreState.FINAL; 8956 break; 8957 } 8958 } 8959 8960 executeNextState(nextState); 8961 } 8962 8963 // A call to agent.doRestore() or agent.doRestoreFinished() has timed out 8964 @Override 8965 public void handleTimeout() { 8966 Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName); 8967 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8968 mCurrentPackage.packageName, "restore timeout"); 8969 // Handle like an agent that threw on invocation: wipe it and go on to the next 8970 keyValueAgentErrorCleanup(); 8971 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8972 } 8973 8974 void executeNextState(UnifiedRestoreState nextState) { 8975 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 8976 + this + " nextState=" + nextState); 8977 mState = nextState; 8978 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 8979 mBackupHandler.sendMessage(msg); 8980 } 8981 8982 // restore observer support 8983 void sendStartRestore(int numPackages) { 8984 if (mObserver != null) { 8985 try { 8986 mObserver.restoreStarting(numPackages); 8987 } catch (RemoteException e) { 8988 Slog.w(TAG, "Restore observer went away: startRestore"); 8989 mObserver = null; 8990 } 8991 } 8992 } 8993 8994 void sendOnRestorePackage(String name) { 8995 if (mObserver != null) { 8996 if (mObserver != null) { 8997 try { 8998 mObserver.onUpdate(mCount, name); 8999 } catch (RemoteException e) { 9000 Slog.d(TAG, "Restore observer died in onUpdate"); 9001 mObserver = null; 9002 } 9003 } 9004 } 9005 } 9006 9007 void sendEndRestore() { 9008 if (mObserver != null) { 9009 try { 9010 mObserver.restoreFinished(mStatus); 9011 } catch (RemoteException e) { 9012 Slog.w(TAG, "Restore observer went away: endRestore"); 9013 mObserver = null; 9014 } 9015 } 9016 } 9017 } 9018 9019 class PerformClearTask implements Runnable { 9020 IBackupTransport mTransport; 9021 PackageInfo mPackage; 9022 9023 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) { 9024 mTransport = transport; 9025 mPackage = packageInfo; 9026 } 9027 9028 public void run() { 9029 try { 9030 // Clear the on-device backup state to ensure a full backup next time 9031 File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); 9032 File stateFile = new File(stateDir, mPackage.packageName); 9033 stateFile.delete(); 9034 9035 // Tell the transport to remove all the persistent storage for the app 9036 // TODO - need to handle failures 9037 mTransport.clearBackupData(mPackage); 9038 } catch (RemoteException e) { 9039 // can't happen; the transport is local 9040 } catch (Exception e) { 9041 Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage); 9042 } finally { 9043 try { 9044 // TODO - need to handle failures 9045 mTransport.finishBackup(); 9046 } catch (RemoteException e) { 9047 // can't happen; the transport is local 9048 } 9049 9050 // Last but not least, release the cpu 9051 mWakelock.release(); 9052 } 9053 } 9054 } 9055 9056 class PerformInitializeTask implements Runnable { 9057 HashSet<String> mQueue; 9058 9059 PerformInitializeTask(HashSet<String> transportNames) { 9060 mQueue = transportNames; 9061 } 9062 9063 public void run() { 9064 try { 9065 for (String transportName : mQueue) { 9066 IBackupTransport transport = getTransport(transportName); 9067 if (transport == null) { 9068 Slog.e(TAG, "Requested init for " + transportName + " but not found"); 9069 continue; 9070 } 9071 9072 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); 9073 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName()); 9074 long startRealtime = SystemClock.elapsedRealtime(); 9075 int status = transport.initializeDevice(); 9076 9077 if (status == BackupTransport.TRANSPORT_OK) { 9078 status = transport.finishBackup(); 9079 } 9080 9081 // Okay, the wipe really happened. Clean up our local bookkeeping. 9082 if (status == BackupTransport.TRANSPORT_OK) { 9083 Slog.i(TAG, "Device init successful"); 9084 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); 9085 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 9086 resetBackupState(new File(mBaseStateDir, transport.transportDirName())); 9087 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); 9088 synchronized (mQueueLock) { 9089 recordInitPendingLocked(false, transportName); 9090 } 9091 } else { 9092 // If this didn't work, requeue this one and try again 9093 // after a suitable interval 9094 Slog.e(TAG, "Transport error in initializeDevice()"); 9095 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 9096 synchronized (mQueueLock) { 9097 recordInitPendingLocked(true, transportName); 9098 } 9099 // do this via another alarm to make sure of the wakelock states 9100 long delay = transport.requestBackupTime(); 9101 Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay); 9102 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 9103 System.currentTimeMillis() + delay, mRunInitIntent); 9104 } 9105 } 9106 } catch (RemoteException e) { 9107 // can't happen; the transports are local 9108 } catch (Exception e) { 9109 Slog.e(TAG, "Unexpected error performing init", e); 9110 } finally { 9111 // Done; release the wakelock 9112 mWakelock.release(); 9113 } 9114 } 9115 } 9116 9117 private void dataChangedImpl(String packageName) { 9118 HashSet<String> targets = dataChangedTargets(packageName); 9119 dataChangedImpl(packageName, targets); 9120 } 9121 9122 private void dataChangedImpl(String packageName, HashSet<String> targets) { 9123 // Record that we need a backup pass for the caller. Since multiple callers 9124 // may share a uid, we need to note all candidates within that uid and schedule 9125 // a backup pass for each of them. 9126 if (targets == null) { 9127 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 9128 + " uid=" + Binder.getCallingUid()); 9129 return; 9130 } 9131 9132 synchronized (mQueueLock) { 9133 // Note that this client has made data changes that need to be backed up 9134 if (targets.contains(packageName)) { 9135 // Add the caller to the set of pending backups. If there is 9136 // one already there, then overwrite it, but no harm done. 9137 BackupRequest req = new BackupRequest(packageName); 9138 if (mPendingBackups.put(packageName, req) == null) { 9139 if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName); 9140 9141 // Journal this request in case of crash. The put() 9142 // operation returned null when this package was not already 9143 // in the set; we want to avoid touching the disk redundantly. 9144 writeToJournalLocked(packageName); 9145 } 9146 } 9147 } 9148 9149 // ...and schedule a backup pass if necessary 9150 KeyValueBackupJob.schedule(mContext); 9151 } 9152 9153 // Note: packageName is currently unused, but may be in the future 9154 private HashSet<String> dataChangedTargets(String packageName) { 9155 // If the caller does not hold the BACKUP permission, it can only request a 9156 // backup of its own data. 9157 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 9158 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 9159 synchronized (mBackupParticipants) { 9160 return mBackupParticipants.get(Binder.getCallingUid()); 9161 } 9162 } 9163 9164 // a caller with full permission can ask to back up any participating app 9165 HashSet<String> targets = new HashSet<String>(); 9166 if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) { 9167 targets.add(PACKAGE_MANAGER_SENTINEL); 9168 } else { 9169 synchronized (mBackupParticipants) { 9170 int N = mBackupParticipants.size(); 9171 for (int i = 0; i < N; i++) { 9172 HashSet<String> s = mBackupParticipants.valueAt(i); 9173 if (s != null) { 9174 targets.addAll(s); 9175 } 9176 } 9177 } 9178 } 9179 return targets; 9180 } 9181 9182 private void writeToJournalLocked(String str) { 9183 RandomAccessFile out = null; 9184 try { 9185 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir); 9186 out = new RandomAccessFile(mJournal, "rws"); 9187 out.seek(out.length()); 9188 out.writeUTF(str); 9189 } catch (IOException e) { 9190 Slog.e(TAG, "Can't write " + str + " to backup journal", e); 9191 mJournal = null; 9192 } finally { 9193 try { if (out != null) out.close(); } catch (IOException e) {} 9194 } 9195 } 9196 9197 // ----- IBackupManager binder interface ----- 9198 9199 public void dataChanged(final String packageName) { 9200 final int callingUserHandle = UserHandle.getCallingUserId(); 9201 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9202 // TODO: http://b/22388012 9203 // App is running under a non-owner user profile. For now, we do not back 9204 // up data from secondary user profiles. 9205 // TODO: backups for all user profiles although don't add backup for profiles 9206 // without adding admin control in DevicePolicyManager. 9207 if (MORE_DEBUG) { 9208 Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " 9209 + callingUserHandle); 9210 } 9211 return; 9212 } 9213 9214 final HashSet<String> targets = dataChangedTargets(packageName); 9215 if (targets == null) { 9216 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 9217 + " uid=" + Binder.getCallingUid()); 9218 return; 9219 } 9220 9221 mBackupHandler.post(new Runnable() { 9222 public void run() { 9223 dataChangedImpl(packageName, targets); 9224 } 9225 }); 9226 } 9227 9228 // Clear the given package's backup data from the current transport 9229 public void clearBackupData(String transportName, String packageName) { 9230 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); 9231 PackageInfo info; 9232 try { 9233 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 9234 } catch (NameNotFoundException e) { 9235 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); 9236 return; 9237 } 9238 9239 // If the caller does not hold the BACKUP permission, it can only request a 9240 // wipe of its own backed-up data. 9241 HashSet<String> apps; 9242 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 9243 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 9244 apps = mBackupParticipants.get(Binder.getCallingUid()); 9245 } else { 9246 // a caller with full permission can ask to back up any participating app 9247 // !!! TODO: allow data-clear of ANY app? 9248 if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps"); 9249 apps = new HashSet<String>(); 9250 int N = mBackupParticipants.size(); 9251 for (int i = 0; i < N; i++) { 9252 HashSet<String> s = mBackupParticipants.valueAt(i); 9253 if (s != null) { 9254 apps.addAll(s); 9255 } 9256 } 9257 } 9258 9259 // Is the given app an available participant? 9260 if (apps.contains(packageName)) { 9261 // found it; fire off the clear request 9262 if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process"); 9263 mBackupHandler.removeMessages(MSG_RETRY_CLEAR); 9264 synchronized (mQueueLock) { 9265 final IBackupTransport transport = getTransport(transportName); 9266 if (transport == null) { 9267 // transport is currently unavailable -- make sure to retry 9268 Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR, 9269 new ClearRetryParams(transportName, packageName)); 9270 mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL); 9271 return; 9272 } 9273 long oldId = Binder.clearCallingIdentity(); 9274 mWakelock.acquire(); 9275 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, 9276 new ClearParams(transport, info)); 9277 mBackupHandler.sendMessage(msg); 9278 Binder.restoreCallingIdentity(oldId); 9279 } 9280 } 9281 } 9282 9283 // Run a backup pass immediately for any applications that have declared 9284 // that they have pending updates. 9285 public void backupNow() { 9286 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow"); 9287 9288 if (mPowerManager.isPowerSaveMode()) { 9289 if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode"); 9290 KeyValueBackupJob.schedule(mContext); // try again in several hours 9291 } else { 9292 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); 9293 synchronized (mQueueLock) { 9294 // Fire the intent that kicks off the whole shebang... 9295 try { 9296 mRunBackupIntent.send(); 9297 } catch (PendingIntent.CanceledException e) { 9298 // should never happen 9299 Slog.e(TAG, "run-backup intent cancelled!"); 9300 } 9301 9302 // ...and cancel any pending scheduled job, because we've just superseded it 9303 KeyValueBackupJob.cancel(mContext); 9304 } 9305 } 9306 } 9307 9308 boolean deviceIsProvisioned() { 9309 final ContentResolver resolver = mContext.getContentResolver(); 9310 return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); 9311 } 9312 9313 // Run a *full* backup pass for the given packages, writing the resulting data stream 9314 // to the supplied file descriptor. This method is synchronous and does not return 9315 // to the caller until the backup has been completed. 9316 // 9317 // This is the variant used by 'adb backup'; it requires on-screen confirmation 9318 // by the user because it can be used to offload data over untrusted USB. 9319 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, 9320 boolean includeObbs, boolean includeShared, boolean doWidgets, 9321 boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) { 9322 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); 9323 9324 final int callingUserHandle = UserHandle.getCallingUserId(); 9325 // TODO: http://b/22388012 9326 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9327 throw new IllegalStateException("Backup supported only for the device owner"); 9328 } 9329 9330 // Validate 9331 if (!doAllApps) { 9332 if (!includeShared) { 9333 // If we're backing up shared data (sdcard or equivalent), then we can run 9334 // without any supplied app names. Otherwise, we'd be doing no work, so 9335 // report the error. 9336 if (pkgList == null || pkgList.length == 0) { 9337 throw new IllegalArgumentException( 9338 "Backup requested but neither shared nor any apps named"); 9339 } 9340 } 9341 } 9342 9343 long oldId = Binder.clearCallingIdentity(); 9344 try { 9345 // Doesn't make sense to do a full backup prior to setup 9346 if (!deviceIsProvisioned()) { 9347 Slog.i(TAG, "Full backup not supported before setup"); 9348 return; 9349 } 9350 9351 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks 9352 + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps 9353 + " system=" + includeSystem + " pkgs=" + pkgList); 9354 Slog.i(TAG, "Beginning full backup..."); 9355 9356 FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs, 9357 includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList); 9358 final int token = generateToken(); 9359 synchronized (mFullConfirmations) { 9360 mFullConfirmations.put(token, params); 9361 } 9362 9363 // start up the confirmation UI 9364 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token); 9365 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { 9366 Slog.e(TAG, "Unable to launch full backup confirmation"); 9367 mFullConfirmations.delete(token); 9368 return; 9369 } 9370 9371 // make sure the screen is lit for the user interaction 9372 mPowerManager.userActivity(SystemClock.uptimeMillis(), 9373 PowerManager.USER_ACTIVITY_EVENT_OTHER, 9374 0); 9375 9376 // start the confirmation countdown 9377 startConfirmationTimeout(token, params); 9378 9379 // wait for the backup to be performed 9380 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion..."); 9381 waitForCompletion(params); 9382 } finally { 9383 try { 9384 fd.close(); 9385 } catch (IOException e) { 9386 // just eat it 9387 } 9388 Binder.restoreCallingIdentity(oldId); 9389 Slog.d(TAG, "Full backup processing complete."); 9390 } 9391 } 9392 9393 public void fullTransportBackup(String[] pkgNames) { 9394 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, 9395 "fullTransportBackup"); 9396 9397 final int callingUserHandle = UserHandle.getCallingUserId(); 9398 // TODO: http://b/22388012 9399 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9400 throw new IllegalStateException("Restore supported only for the device owner"); 9401 } 9402 9403 if (!fullBackupAllowable(getTransport(mCurrentTransport))) { 9404 Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?"); 9405 } else { 9406 if (DEBUG) { 9407 Slog.d(TAG, "fullTransportBackup()"); 9408 } 9409 9410 final long oldId = Binder.clearCallingIdentity(); 9411 try { 9412 CountDownLatch latch = new CountDownLatch(1); 9413 PerformFullTransportBackupTask task = new PerformFullTransportBackupTask(null, 9414 pkgNames, false, null, latch, null, false /* userInitiated */); 9415 // Acquiring wakelock for PerformFullTransportBackupTask before its start. 9416 mWakelock.acquire(); 9417 (new Thread(task, "full-transport-master")).start(); 9418 do { 9419 try { 9420 latch.await(); 9421 break; 9422 } catch (InterruptedException e) { 9423 // Just go back to waiting for the latch to indicate completion 9424 } 9425 } while (true); 9426 9427 // We just ran a backup on these packages, so kick them to the end of the queue 9428 final long now = System.currentTimeMillis(); 9429 for (String pkg : pkgNames) { 9430 enqueueFullBackup(pkg, now); 9431 } 9432 } finally { 9433 Binder.restoreCallingIdentity(oldId); 9434 } 9435 } 9436 9437 if (DEBUG) { 9438 Slog.d(TAG, "Done with full transport backup."); 9439 } 9440 } 9441 9442 public void fullRestore(ParcelFileDescriptor fd) { 9443 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); 9444 9445 final int callingUserHandle = UserHandle.getCallingUserId(); 9446 // TODO: http://b/22388012 9447 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9448 throw new IllegalStateException("Restore supported only for the device owner"); 9449 } 9450 9451 long oldId = Binder.clearCallingIdentity(); 9452 9453 try { 9454 // Check whether the device has been provisioned -- we don't handle 9455 // full restores prior to completing the setup process. 9456 if (!deviceIsProvisioned()) { 9457 Slog.i(TAG, "Full restore not permitted before setup"); 9458 return; 9459 } 9460 9461 Slog.i(TAG, "Beginning full restore..."); 9462 9463 FullRestoreParams params = new FullRestoreParams(fd); 9464 final int token = generateToken(); 9465 synchronized (mFullConfirmations) { 9466 mFullConfirmations.put(token, params); 9467 } 9468 9469 // start up the confirmation UI 9470 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token); 9471 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) { 9472 Slog.e(TAG, "Unable to launch full restore confirmation"); 9473 mFullConfirmations.delete(token); 9474 return; 9475 } 9476 9477 // make sure the screen is lit for the user interaction 9478 mPowerManager.userActivity(SystemClock.uptimeMillis(), 9479 PowerManager.USER_ACTIVITY_EVENT_OTHER, 9480 0); 9481 9482 // start the confirmation countdown 9483 startConfirmationTimeout(token, params); 9484 9485 // wait for the restore to be performed 9486 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion..."); 9487 waitForCompletion(params); 9488 } finally { 9489 try { 9490 fd.close(); 9491 } catch (IOException e) { 9492 Slog.w(TAG, "Error trying to close fd after full restore: " + e); 9493 } 9494 Binder.restoreCallingIdentity(oldId); 9495 Slog.i(TAG, "Full restore processing complete."); 9496 } 9497 } 9498 9499 boolean startConfirmationUi(int token, String action) { 9500 try { 9501 Intent confIntent = new Intent(action); 9502 confIntent.setClassName("com.android.backupconfirm", 9503 "com.android.backupconfirm.BackupRestoreConfirmation"); 9504 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token); 9505 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 9506 mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM); 9507 } catch (ActivityNotFoundException e) { 9508 return false; 9509 } 9510 return true; 9511 } 9512 9513 void startConfirmationTimeout(int token, FullParams params) { 9514 if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after " 9515 + TIMEOUT_FULL_CONFIRMATION + " millis"); 9516 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, 9517 token, 0, params); 9518 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION); 9519 } 9520 9521 void waitForCompletion(FullParams params) { 9522 synchronized (params.latch) { 9523 while (params.latch.get() == false) { 9524 try { 9525 params.latch.wait(); 9526 } catch (InterruptedException e) { /* never interrupted */ } 9527 } 9528 } 9529 } 9530 9531 void signalFullBackupRestoreCompletion(FullParams params) { 9532 synchronized (params.latch) { 9533 params.latch.set(true); 9534 params.latch.notifyAll(); 9535 } 9536 } 9537 9538 // Confirm that the previously-requested full backup/restore operation can proceed. This 9539 // is used to require a user-facing disclosure about the operation. 9540 public void acknowledgeFullBackupOrRestore(int token, boolean allow, 9541 String curPassword, String encPpassword, IFullBackupRestoreObserver observer) { 9542 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token 9543 + " allow=" + allow); 9544 9545 // TODO: possibly require not just this signature-only permission, but even 9546 // require that the specific designated confirmation-UI app uid is the caller? 9547 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore"); 9548 9549 long oldId = Binder.clearCallingIdentity(); 9550 try { 9551 9552 FullParams params; 9553 synchronized (mFullConfirmations) { 9554 params = mFullConfirmations.get(token); 9555 if (params != null) { 9556 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params); 9557 mFullConfirmations.delete(token); 9558 9559 if (allow) { 9560 final int verb = params instanceof FullBackupParams 9561 ? MSG_RUN_ADB_BACKUP 9562 : MSG_RUN_ADB_RESTORE; 9563 9564 params.observer = observer; 9565 params.curPassword = curPassword; 9566 9567 params.encryptPassword = encPpassword; 9568 9569 if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb); 9570 mWakelock.acquire(); 9571 Message msg = mBackupHandler.obtainMessage(verb, params); 9572 mBackupHandler.sendMessage(msg); 9573 } else { 9574 Slog.w(TAG, "User rejected full backup/restore operation"); 9575 // indicate completion without having actually transferred any data 9576 signalFullBackupRestoreCompletion(params); 9577 } 9578 } else { 9579 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token"); 9580 } 9581 } 9582 } finally { 9583 Binder.restoreCallingIdentity(oldId); 9584 } 9585 } 9586 9587 private static boolean backupSettingMigrated(int userId) { 9588 File base = new File(Environment.getDataDirectory(), "backup"); 9589 File enableFile = new File(base, BACKUP_ENABLE_FILE); 9590 return enableFile.exists(); 9591 } 9592 9593 private static boolean readBackupEnableState(int userId) { 9594 File base = new File(Environment.getDataDirectory(), "backup"); 9595 File enableFile = new File(base, BACKUP_ENABLE_FILE); 9596 if (enableFile.exists()) { 9597 try (FileInputStream fin = new FileInputStream(enableFile)) { 9598 int state = fin.read(); 9599 return state != 0; 9600 } catch (IOException e) { 9601 // can't read the file; fall through to assume disabled 9602 Slog.e(TAG, "Cannot read enable state; assuming disabled"); 9603 } 9604 } else { 9605 if (DEBUG) { 9606 Slog.i(TAG, "isBackupEnabled() => false due to absent settings file"); 9607 } 9608 } 9609 return false; 9610 } 9611 9612 private static void writeBackupEnableState(boolean enable, int userId) { 9613 File base = new File(Environment.getDataDirectory(), "backup"); 9614 File enableFile = new File(base, BACKUP_ENABLE_FILE); 9615 File stage = new File(base, BACKUP_ENABLE_FILE + "-stage"); 9616 FileOutputStream fout = null; 9617 try { 9618 fout = new FileOutputStream(stage); 9619 fout.write(enable ? 1 : 0); 9620 fout.close(); 9621 stage.renameTo(enableFile); 9622 // will be synced immediately by the try-with-resources call to close() 9623 } catch (IOException|RuntimeException e) { 9624 // Whoops; looks like we're doomed. Roll everything out, disabled, 9625 // including the legacy state. 9626 Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: " 9627 + e.getMessage()); 9628 9629 final ContentResolver r = sInstance.mContext.getContentResolver(); 9630 Settings.Secure.putStringForUser(r, 9631 Settings.Secure.BACKUP_ENABLED, null, userId); 9632 enableFile.delete(); 9633 stage.delete(); 9634 } finally { 9635 IoUtils.closeQuietly(fout); 9636 } 9637 } 9638 9639 // Enable/disable backups 9640 public void setBackupEnabled(boolean enable) { 9641 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9642 "setBackupEnabled"); 9643 9644 Slog.i(TAG, "Backup enabled => " + enable); 9645 9646 long oldId = Binder.clearCallingIdentity(); 9647 try { 9648 boolean wasEnabled = mEnabled; 9649 synchronized (this) { 9650 writeBackupEnableState(enable, UserHandle.USER_SYSTEM); 9651 mEnabled = enable; 9652 } 9653 9654 synchronized (mQueueLock) { 9655 if (enable && !wasEnabled && mProvisioned) { 9656 // if we've just been enabled, start scheduling backup passes 9657 KeyValueBackupJob.schedule(mContext); 9658 scheduleNextFullBackupJob(0); 9659 } else if (!enable) { 9660 // No longer enabled, so stop running backups 9661 if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup"); 9662 9663 KeyValueBackupJob.cancel(mContext); 9664 9665 // This also constitutes an opt-out, so we wipe any data for 9666 // this device from the backend. We start that process with 9667 // an alarm in order to guarantee wakelock states. 9668 if (wasEnabled && mProvisioned) { 9669 // NOTE: we currently flush every registered transport, not just 9670 // the currently-active one. 9671 HashSet<String> allTransports; 9672 synchronized (mTransports) { 9673 allTransports = new HashSet<String>(mTransports.keySet()); 9674 } 9675 // build the set of transports for which we are posting an init 9676 for (String transport : allTransports) { 9677 recordInitPendingLocked(true, transport); 9678 } 9679 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 9680 mRunInitIntent); 9681 } 9682 } 9683 } 9684 } finally { 9685 Binder.restoreCallingIdentity(oldId); 9686 } 9687 } 9688 9689 // Enable/disable automatic restore of app data at install time 9690 public void setAutoRestore(boolean doAutoRestore) { 9691 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9692 "setAutoRestore"); 9693 9694 Slog.i(TAG, "Auto restore => " + doAutoRestore); 9695 9696 final long oldId = Binder.clearCallingIdentity(); 9697 try { 9698 synchronized (this) { 9699 Settings.Secure.putInt(mContext.getContentResolver(), 9700 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0); 9701 mAutoRestore = doAutoRestore; 9702 } 9703 } finally { 9704 Binder.restoreCallingIdentity(oldId); 9705 } 9706 } 9707 9708 // Mark the backup service as having been provisioned 9709 public void setBackupProvisioned(boolean available) { 9710 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9711 "setBackupProvisioned"); 9712 /* 9713 * This is now a no-op; provisioning is simply the device's own setup state. 9714 */ 9715 } 9716 9717 // Report whether the backup mechanism is currently enabled 9718 public boolean isBackupEnabled() { 9719 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); 9720 return mEnabled; // no need to synchronize just to read it 9721 } 9722 9723 // Report the name of the currently active transport 9724 public String getCurrentTransport() { 9725 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9726 "getCurrentTransport"); 9727 if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); 9728 return mCurrentTransport; 9729 } 9730 9731 // Report all known, available backup transports 9732 public String[] listAllTransports() { 9733 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); 9734 9735 String[] list = null; 9736 ArrayList<String> known = new ArrayList<String>(); 9737 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { 9738 if (entry.getValue() != null) { 9739 known.add(entry.getKey()); 9740 } 9741 } 9742 9743 if (known.size() > 0) { 9744 list = new String[known.size()]; 9745 known.toArray(list); 9746 } 9747 return list; 9748 } 9749 9750 public String[] getTransportWhitelist() { 9751 // No permission check, intentionally. 9752 String[] whitelist = new String[mTransportWhitelist.size()]; 9753 for (int i = mTransportWhitelist.size() - 1; i >= 0; i--) { 9754 whitelist[i] = mTransportWhitelist.valueAt(i).flattenToShortString(); 9755 } 9756 return whitelist; 9757 } 9758 9759 // Select which transport to use for the next backup operation. 9760 public String selectBackupTransport(String transport) { 9761 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9762 "selectBackupTransport"); 9763 9764 synchronized (mTransports) { 9765 final long oldId = Binder.clearCallingIdentity(); 9766 try { 9767 String prevTransport = mCurrentTransport; 9768 mCurrentTransport = transport; 9769 Settings.Secure.putString(mContext.getContentResolver(), 9770 Settings.Secure.BACKUP_TRANSPORT, transport); 9771 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport 9772 + " returning " + prevTransport); 9773 return prevTransport; 9774 } finally { 9775 Binder.restoreCallingIdentity(oldId); 9776 } 9777 } 9778 } 9779 9780 // Supply the configuration Intent for the given transport. If the name is not one 9781 // of the available transports, or if the transport does not supply any configuration 9782 // UI, the method returns null. 9783 public Intent getConfigurationIntent(String transportName) { 9784 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9785 "getConfigurationIntent"); 9786 9787 synchronized (mTransports) { 9788 final IBackupTransport transport = mTransports.get(transportName); 9789 if (transport != null) { 9790 try { 9791 final Intent intent = transport.configurationIntent(); 9792 if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent " 9793 + intent); 9794 return intent; 9795 } catch (RemoteException e) { 9796 /* fall through to return null */ 9797 } 9798 } 9799 } 9800 9801 return null; 9802 } 9803 9804 // Supply the configuration summary string for the given transport. If the name is 9805 // not one of the available transports, or if the transport does not supply any 9806 // summary / destination string, the method can return null. 9807 // 9808 // This string is used VERBATIM as the summary text of the relevant Settings item! 9809 public String getDestinationString(String transportName) { 9810 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9811 "getDestinationString"); 9812 9813 synchronized (mTransports) { 9814 final IBackupTransport transport = mTransports.get(transportName); 9815 if (transport != null) { 9816 try { 9817 final String text = transport.currentDestinationString(); 9818 if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text); 9819 return text; 9820 } catch (RemoteException e) { 9821 /* fall through to return null */ 9822 } 9823 } 9824 } 9825 9826 return null; 9827 } 9828 9829 // Supply the manage-data intent for the given transport. 9830 public Intent getDataManagementIntent(String transportName) { 9831 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9832 "getDataManagementIntent"); 9833 9834 synchronized (mTransports) { 9835 final IBackupTransport transport = mTransports.get(transportName); 9836 if (transport != null) { 9837 try { 9838 final Intent intent = transport.dataManagementIntent(); 9839 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent " 9840 + intent); 9841 return intent; 9842 } catch (RemoteException e) { 9843 /* fall through to return null */ 9844 } 9845 } 9846 } 9847 9848 return null; 9849 } 9850 9851 // Supply the menu label for affordances that fire the manage-data intent 9852 // for the given transport. 9853 public String getDataManagementLabel(String transportName) { 9854 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9855 "getDataManagementLabel"); 9856 9857 synchronized (mTransports) { 9858 final IBackupTransport transport = mTransports.get(transportName); 9859 if (transport != null) { 9860 try { 9861 final String text = transport.dataManagementLabel(); 9862 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text); 9863 return text; 9864 } catch (RemoteException e) { 9865 /* fall through to return null */ 9866 } 9867 } 9868 } 9869 9870 return null; 9871 } 9872 9873 // Callback: a requested backup agent has been instantiated. This should only 9874 // be called from the Activity Manager. 9875 public void agentConnected(String packageName, IBinder agentBinder) { 9876 synchronized(mAgentConnectLock) { 9877 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 9878 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); 9879 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); 9880 mConnectedAgent = agent; 9881 mConnecting = false; 9882 } else { 9883 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9884 + " claiming agent connected"); 9885 } 9886 mAgentConnectLock.notifyAll(); 9887 } 9888 } 9889 9890 // Callback: a backup agent has failed to come up, or has unexpectedly quit. 9891 // If the agent failed to come up in the first place, the agentBinder argument 9892 // will be null. This should only be called from the Activity Manager. 9893 public void agentDisconnected(String packageName) { 9894 // TODO: handle backup being interrupted 9895 synchronized(mAgentConnectLock) { 9896 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 9897 mConnectedAgent = null; 9898 mConnecting = false; 9899 } else { 9900 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9901 + " claiming agent disconnected"); 9902 } 9903 mAgentConnectLock.notifyAll(); 9904 } 9905 } 9906 9907 // An application being installed will need a restore pass, then the Package Manager 9908 // will need to be told when the restore is finished. 9909 public void restoreAtInstall(String packageName, int token) { 9910 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 9911 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9912 + " attemping install-time restore"); 9913 return; 9914 } 9915 9916 boolean skip = false; 9917 9918 long restoreSet = getAvailableRestoreToken(packageName); 9919 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName 9920 + " token=" + Integer.toHexString(token) 9921 + " restoreSet=" + Long.toHexString(restoreSet)); 9922 if (restoreSet == 0) { 9923 if (MORE_DEBUG) Slog.i(TAG, "No restore set"); 9924 skip = true; 9925 } 9926 9927 // Do we have a transport to fetch data for us? 9928 IBackupTransport transport = getTransport(mCurrentTransport); 9929 if (transport == null) { 9930 if (DEBUG) Slog.w(TAG, "No transport"); 9931 skip = true; 9932 } 9933 9934 if (!mAutoRestore) { 9935 if (DEBUG) { 9936 Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore); 9937 } 9938 skip = true; 9939 } 9940 9941 if (!skip) { 9942 try { 9943 // okay, we're going to attempt a restore of this package from this restore set. 9944 // The eventual message back into the Package Manager to run the post-install 9945 // steps for 'token' will be issued from the restore handling code. 9946 9947 // This can throw and so *must* happen before the wakelock is acquired 9948 String dirName = transport.transportDirName(); 9949 9950 mWakelock.acquire(); 9951 if (MORE_DEBUG) { 9952 Slog.d(TAG, "Restore at install of " + packageName); 9953 } 9954 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9955 msg.obj = new RestoreParams(transport, dirName, null, 9956 restoreSet, packageName, token); 9957 mBackupHandler.sendMessage(msg); 9958 } catch (RemoteException e) { 9959 // Binding to the transport broke; back off and proceed with the installation. 9960 Slog.e(TAG, "Unable to contact transport"); 9961 skip = true; 9962 } 9963 } 9964 9965 if (skip) { 9966 // Auto-restore disabled or no way to attempt a restore; just tell the Package 9967 // Manager to proceed with the post-install handling for this package. 9968 if (DEBUG) Slog.v(TAG, "Finishing install immediately"); 9969 try { 9970 mPackageManagerBinder.finishPackageInstall(token, false); 9971 } catch (RemoteException e) { /* can't happen */ } 9972 } 9973 } 9974 9975 // Hand off a restore session 9976 public IRestoreSession beginRestoreSession(String packageName, String transport) { 9977 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName 9978 + " transport=" + transport); 9979 9980 boolean needPermission = true; 9981 if (transport == null) { 9982 transport = mCurrentTransport; 9983 9984 if (packageName != null) { 9985 PackageInfo app = null; 9986 try { 9987 app = mPackageManager.getPackageInfo(packageName, 0); 9988 } catch (NameNotFoundException nnf) { 9989 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 9990 throw new IllegalArgumentException("Package " + packageName + " not found"); 9991 } 9992 9993 if (app.applicationInfo.uid == Binder.getCallingUid()) { 9994 // So: using the current active transport, and the caller has asked 9995 // that its own package will be restored. In this narrow use case 9996 // we do not require the caller to hold the permission. 9997 needPermission = false; 9998 } 9999 } 10000 } 10001 10002 if (needPermission) { 10003 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10004 "beginRestoreSession"); 10005 } else { 10006 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed"); 10007 } 10008 10009 synchronized(this) { 10010 if (mActiveRestoreSession != null) { 10011 Slog.i(TAG, "Restore session requested but one already active"); 10012 return null; 10013 } 10014 if (mBackupRunning) { 10015 Slog.i(TAG, "Restore session requested but currently running backups"); 10016 return null; 10017 } 10018 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport); 10019 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 10020 } 10021 return mActiveRestoreSession; 10022 } 10023 10024 void clearRestoreSession(ActiveRestoreSession currentSession) { 10025 synchronized(this) { 10026 if (currentSession != mActiveRestoreSession) { 10027 Slog.e(TAG, "ending non-current restore session"); 10028 } else { 10029 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout"); 10030 mActiveRestoreSession = null; 10031 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10032 } 10033 } 10034 } 10035 10036 // Note that a currently-active backup agent has notified us that it has 10037 // completed the given outstanding asynchronous backup/restore operation. 10038 public void opComplete(int token, long result) { 10039 if (MORE_DEBUG) { 10040 Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result); 10041 } 10042 Operation op = null; 10043 synchronized (mCurrentOpLock) { 10044 op = mCurrentOperations.get(token); 10045 if (op != null) { 10046 if (op.state == OP_TIMEOUT) { 10047 // The operation already timed out, and this is a late response. Tidy up 10048 // and ignore it; we've already dealt with the timeout. 10049 op = null; 10050 mCurrentOperations.delete(token); 10051 } else { 10052 op.state = OP_ACKNOWLEDGED; 10053 } 10054 } 10055 mCurrentOpLock.notifyAll(); 10056 } 10057 10058 // The completion callback, if any, is invoked on the handler 10059 if (op != null && op.callback != null) { 10060 Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result); 10061 Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult); 10062 mBackupHandler.sendMessage(msg); 10063 } 10064 } 10065 10066 public boolean isAppEligibleForBackup(String packageName) { 10067 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10068 "isAppEligibleForBackup"); 10069 try { 10070 PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 10071 PackageManager.GET_SIGNATURES); 10072 if (!appIsEligibleForBackup(packageInfo.applicationInfo) || 10073 appIsStopped(packageInfo.applicationInfo)) { 10074 return false; 10075 } 10076 IBackupTransport transport = getTransport(mCurrentTransport); 10077 if (transport != null) { 10078 try { 10079 return transport.isAppEligibleForBackup(packageInfo, 10080 appGetsFullBackup(packageInfo)); 10081 } catch (RemoteException e) { 10082 Slog.e(TAG, "Unable to contact transport"); 10083 } 10084 } 10085 // If transport is not present we couldn't tell that the package is not eligible. 10086 return true; 10087 } catch (NameNotFoundException e) { 10088 return false; 10089 } 10090 } 10091 10092 // ----- Restore session ----- 10093 10094 class ActiveRestoreSession extends IRestoreSession.Stub { 10095 private static final String TAG = "RestoreSession"; 10096 10097 private String mPackageName; 10098 private IBackupTransport mRestoreTransport = null; 10099 RestoreSet[] mRestoreSets = null; 10100 boolean mEnded = false; 10101 boolean mTimedOut = false; 10102 10103 ActiveRestoreSession(String packageName, String transport) { 10104 mPackageName = packageName; 10105 mRestoreTransport = getTransport(transport); 10106 } 10107 10108 public void markTimedOut() { 10109 mTimedOut = true; 10110 } 10111 10112 // --- Binder interface --- 10113 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) { 10114 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10115 "getAvailableRestoreSets"); 10116 if (observer == null) { 10117 throw new IllegalArgumentException("Observer must not be null"); 10118 } 10119 10120 if (mEnded) { 10121 throw new IllegalStateException("Restore session already ended"); 10122 } 10123 10124 if (mTimedOut) { 10125 Slog.i(TAG, "Session already timed out"); 10126 return -1; 10127 } 10128 10129 long oldId = Binder.clearCallingIdentity(); 10130 try { 10131 if (mRestoreTransport == null) { 10132 Slog.w(TAG, "Null transport getting restore sets"); 10133 return -1; 10134 } 10135 10136 // We know we're doing legit work now, so halt the timeout 10137 // until we're done. It gets started again when the result 10138 // comes in. 10139 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10140 10141 // spin off the transport request to our service thread 10142 mWakelock.acquire(); 10143 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS, 10144 new RestoreGetSetsParams(mRestoreTransport, this, observer)); 10145 mBackupHandler.sendMessage(msg); 10146 return 0; 10147 } catch (Exception e) { 10148 Slog.e(TAG, "Error in getAvailableRestoreSets", e); 10149 return -1; 10150 } finally { 10151 Binder.restoreCallingIdentity(oldId); 10152 } 10153 } 10154 10155 public synchronized int restoreAll(long token, IRestoreObserver observer) { 10156 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10157 "performRestore"); 10158 10159 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token) 10160 + " observer=" + observer); 10161 10162 if (mEnded) { 10163 throw new IllegalStateException("Restore session already ended"); 10164 } 10165 10166 if (mTimedOut) { 10167 Slog.i(TAG, "Session already timed out"); 10168 return -1; 10169 } 10170 10171 if (mRestoreTransport == null || mRestoreSets == null) { 10172 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 10173 return -1; 10174 } 10175 10176 if (mPackageName != null) { 10177 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 10178 return -1; 10179 } 10180 10181 String dirName; 10182 try { 10183 dirName = mRestoreTransport.transportDirName(); 10184 } catch (RemoteException e) { 10185 // Transport went AWOL; fail. 10186 Slog.e(TAG, "Unable to contact transport for restore"); 10187 return -1; 10188 } 10189 10190 synchronized (mQueueLock) { 10191 for (int i = 0; i < mRestoreSets.length; i++) { 10192 if (token == mRestoreSets[i].token) { 10193 // Real work, so stop the session timeout until we finalize the restore 10194 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10195 10196 long oldId = Binder.clearCallingIdentity(); 10197 mWakelock.acquire(); 10198 if (MORE_DEBUG) { 10199 Slog.d(TAG, "restoreAll() kicking off"); 10200 } 10201 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 10202 msg.obj = new RestoreParams(mRestoreTransport, dirName, 10203 observer, token); 10204 mBackupHandler.sendMessage(msg); 10205 Binder.restoreCallingIdentity(oldId); 10206 return 0; 10207 } 10208 } 10209 } 10210 10211 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 10212 return -1; 10213 } 10214 10215 // Restores of more than a single package are treated as 'system' restores 10216 public synchronized int restoreSome(long token, IRestoreObserver observer, 10217 String[] packages) { 10218 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10219 "performRestore"); 10220 10221 if (DEBUG) { 10222 StringBuilder b = new StringBuilder(128); 10223 b.append("restoreSome token="); 10224 b.append(Long.toHexString(token)); 10225 b.append(" observer="); 10226 b.append(observer.toString()); 10227 b.append(" packages="); 10228 if (packages == null) { 10229 b.append("null"); 10230 } else { 10231 b.append('{'); 10232 boolean first = true; 10233 for (String s : packages) { 10234 if (!first) { 10235 b.append(", "); 10236 } else first = false; 10237 b.append(s); 10238 } 10239 b.append('}'); 10240 } 10241 Slog.d(TAG, b.toString()); 10242 } 10243 10244 if (mEnded) { 10245 throw new IllegalStateException("Restore session already ended"); 10246 } 10247 10248 if (mTimedOut) { 10249 Slog.i(TAG, "Session already timed out"); 10250 return -1; 10251 } 10252 10253 if (mRestoreTransport == null || mRestoreSets == null) { 10254 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 10255 return -1; 10256 } 10257 10258 if (mPackageName != null) { 10259 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 10260 return -1; 10261 } 10262 10263 String dirName; 10264 try { 10265 dirName = mRestoreTransport.transportDirName(); 10266 } catch (RemoteException e) { 10267 // Transport went AWOL; fail. 10268 Slog.e(TAG, "Unable to contact transport for restore"); 10269 return -1; 10270 } 10271 10272 synchronized (mQueueLock) { 10273 for (int i = 0; i < mRestoreSets.length; i++) { 10274 if (token == mRestoreSets[i].token) { 10275 // Stop the session timeout until we finalize the restore 10276 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10277 10278 long oldId = Binder.clearCallingIdentity(); 10279 mWakelock.acquire(); 10280 if (MORE_DEBUG) { 10281 Slog.d(TAG, "restoreSome() of " + packages.length + " packages"); 10282 } 10283 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 10284 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, 10285 packages, packages.length > 1); 10286 mBackupHandler.sendMessage(msg); 10287 Binder.restoreCallingIdentity(oldId); 10288 return 0; 10289 } 10290 } 10291 } 10292 10293 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 10294 return -1; 10295 } 10296 10297 public synchronized int restorePackage(String packageName, IRestoreObserver observer) { 10298 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); 10299 10300 if (mEnded) { 10301 throw new IllegalStateException("Restore session already ended"); 10302 } 10303 10304 if (mTimedOut) { 10305 Slog.i(TAG, "Session already timed out"); 10306 return -1; 10307 } 10308 10309 if (mPackageName != null) { 10310 if (! mPackageName.equals(packageName)) { 10311 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName 10312 + " on session for package " + mPackageName); 10313 return -1; 10314 } 10315 } 10316 10317 PackageInfo app = null; 10318 try { 10319 app = mPackageManager.getPackageInfo(packageName, 0); 10320 } catch (NameNotFoundException nnf) { 10321 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 10322 return -1; 10323 } 10324 10325 // If the caller is not privileged and is not coming from the target 10326 // app's uid, throw a permission exception back to the caller. 10327 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP, 10328 Binder.getCallingPid(), Binder.getCallingUid()); 10329 if ((perm == PackageManager.PERMISSION_DENIED) && 10330 (app.applicationInfo.uid != Binder.getCallingUid())) { 10331 Slog.w(TAG, "restorePackage: bad packageName=" + packageName 10332 + " or calling uid=" + Binder.getCallingUid()); 10333 throw new SecurityException("No permission to restore other packages"); 10334 } 10335 10336 // So far so good; we're allowed to try to restore this package. 10337 long oldId = Binder.clearCallingIdentity(); 10338 try { 10339 // Check whether there is data for it in the current dataset, falling back 10340 // to the ancestral dataset if not. 10341 long token = getAvailableRestoreToken(packageName); 10342 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName 10343 + " token=" + Long.toHexString(token)); 10344 10345 // If we didn't come up with a place to look -- no ancestral dataset and 10346 // the app has never been backed up from this device -- there's nothing 10347 // to do but return failure. 10348 if (token == 0) { 10349 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring"); 10350 return -1; 10351 } 10352 10353 String dirName; 10354 try { 10355 dirName = mRestoreTransport.transportDirName(); 10356 } catch (RemoteException e) { 10357 // Transport went AWOL; fail. 10358 Slog.e(TAG, "Unable to contact transport for restore"); 10359 return -1; 10360 } 10361 10362 // Stop the session timeout until we finalize the restore 10363 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10364 10365 // Ready to go: enqueue the restore request and claim success 10366 mWakelock.acquire(); 10367 if (MORE_DEBUG) { 10368 Slog.d(TAG, "restorePackage() : " + packageName); 10369 } 10370 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 10371 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app); 10372 mBackupHandler.sendMessage(msg); 10373 } finally { 10374 Binder.restoreCallingIdentity(oldId); 10375 } 10376 return 0; 10377 } 10378 10379 // Posted to the handler to tear down a restore session in a cleanly synchronized way 10380 class EndRestoreRunnable implements Runnable { 10381 BackupManagerService mBackupManager; 10382 ActiveRestoreSession mSession; 10383 10384 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) { 10385 mBackupManager = manager; 10386 mSession = session; 10387 } 10388 10389 public void run() { 10390 // clean up the session's bookkeeping 10391 synchronized (mSession) { 10392 mSession.mRestoreTransport = null; 10393 mSession.mEnded = true; 10394 } 10395 10396 // clean up the BackupManagerImpl side of the bookkeeping 10397 // and cancel any pending timeout message 10398 mBackupManager.clearRestoreSession(mSession); 10399 } 10400 } 10401 10402 public synchronized void endRestoreSession() { 10403 if (DEBUG) Slog.d(TAG, "endRestoreSession"); 10404 10405 if (mTimedOut) { 10406 Slog.i(TAG, "Session already timed out"); 10407 return; 10408 } 10409 10410 if (mEnded) { 10411 throw new IllegalStateException("Restore session already ended"); 10412 } 10413 10414 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this)); 10415 } 10416 } 10417 10418 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 10419 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 10420 10421 long identityToken = Binder.clearCallingIdentity(); 10422 try { 10423 if (args != null) { 10424 for (String arg : args) { 10425 if ("-h".equals(arg)) { 10426 pw.println("'dumpsys backup' optional arguments:"); 10427 pw.println(" -h : this help text"); 10428 pw.println(" a[gents] : dump information about defined backup agents"); 10429 return; 10430 } else if ("agents".startsWith(arg)) { 10431 dumpAgents(pw); 10432 return; 10433 } 10434 } 10435 } 10436 dumpInternal(pw); 10437 } finally { 10438 Binder.restoreCallingIdentity(identityToken); 10439 } 10440 } 10441 10442 private void dumpAgents(PrintWriter pw) { 10443 List<PackageInfo> agentPackages = allAgentPackages(); 10444 pw.println("Defined backup agents:"); 10445 for (PackageInfo pkg : agentPackages) { 10446 pw.print(" "); 10447 pw.print(pkg.packageName); pw.println(':'); 10448 pw.print(" "); pw.println(pkg.applicationInfo.backupAgentName); 10449 } 10450 } 10451 10452 private void dumpInternal(PrintWriter pw) { 10453 synchronized (mQueueLock) { 10454 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") 10455 + " / " + (!mProvisioned ? "not " : "") + "provisioned / " 10456 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); 10457 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); 10458 if (mBackupRunning) pw.println("Backup currently running"); 10459 pw.println("Last backup pass started: " + mLastBackupPass 10460 + " (now = " + System.currentTimeMillis() + ')'); 10461 pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled()); 10462 10463 pw.println("Transport whitelist:"); 10464 for (ComponentName transport : mTransportWhitelist) { 10465 pw.print(" "); 10466 pw.println(transport.flattenToShortString()); 10467 } 10468 10469 pw.println("Available transports:"); 10470 final String[] transports = listAllTransports(); 10471 if (transports != null) { 10472 for (String t : listAllTransports()) { 10473 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t); 10474 try { 10475 IBackupTransport transport = getTransport(t); 10476 File dir = new File(mBaseStateDir, transport.transportDirName()); 10477 pw.println(" destination: " + transport.currentDestinationString()); 10478 pw.println(" intent: " + transport.configurationIntent()); 10479 for (File f : dir.listFiles()) { 10480 pw.println(" " + f.getName() + " - " + f.length() + " state bytes"); 10481 } 10482 } catch (Exception e) { 10483 Slog.e(TAG, "Error in transport", e); 10484 pw.println(" Error: " + e); 10485 } 10486 } 10487 } 10488 10489 pw.println("Pending init: " + mPendingInits.size()); 10490 for (String s : mPendingInits) { 10491 pw.println(" " + s); 10492 } 10493 10494 if (DEBUG_BACKUP_TRACE) { 10495 synchronized (mBackupTrace) { 10496 if (!mBackupTrace.isEmpty()) { 10497 pw.println("Most recent backup trace:"); 10498 for (String s : mBackupTrace) { 10499 pw.println(" " + s); 10500 } 10501 } 10502 } 10503 } 10504 10505 pw.print("Ancestral: "); pw.println(Long.toHexString(mAncestralToken)); 10506 pw.print("Current: "); pw.println(Long.toHexString(mCurrentToken)); 10507 10508 int N = mBackupParticipants.size(); 10509 pw.println("Participants:"); 10510 for (int i=0; i<N; i++) { 10511 int uid = mBackupParticipants.keyAt(i); 10512 pw.print(" uid: "); 10513 pw.println(uid); 10514 HashSet<String> participants = mBackupParticipants.valueAt(i); 10515 for (String app: participants) { 10516 pw.println(" " + app); 10517 } 10518 } 10519 10520 pw.println("Ancestral packages: " 10521 + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); 10522 if (mAncestralPackages != null) { 10523 for (String pkg : mAncestralPackages) { 10524 pw.println(" " + pkg); 10525 } 10526 } 10527 10528 pw.println("Ever backed up: " + mEverStoredApps.size()); 10529 for (String pkg : mEverStoredApps) { 10530 pw.println(" " + pkg); 10531 } 10532 10533 pw.println("Pending key/value backup: " + mPendingBackups.size()); 10534 for (BackupRequest req : mPendingBackups.values()) { 10535 pw.println(" " + req); 10536 } 10537 10538 pw.println("Full backup queue:" + mFullBackupQueue.size()); 10539 for (FullBackupEntry entry : mFullBackupQueue) { 10540 pw.print(" "); pw.print(entry.lastBackup); 10541 pw.print(" : "); pw.println(entry.packageName); 10542 } 10543 } 10544 } 10545 10546 private static void sendBackupOnUpdate(IBackupObserver observer, String packageName, 10547 BackupProgress progress) { 10548 if (observer != null) { 10549 try { 10550 observer.onUpdate(packageName, progress); 10551 } catch (RemoteException e) { 10552 if (DEBUG) { 10553 Slog.w(TAG, "Backup observer went away: onUpdate"); 10554 } 10555 } 10556 } 10557 } 10558 10559 private static void sendBackupOnPackageResult(IBackupObserver observer, String packageName, 10560 int status) { 10561 if (observer != null) { 10562 try { 10563 observer.onResult(packageName, status); 10564 } catch (RemoteException e) { 10565 if (DEBUG) { 10566 Slog.w(TAG, "Backup observer went away: onResult"); 10567 } 10568 } 10569 } 10570 } 10571 10572 private static void sendBackupFinished(IBackupObserver observer, int status) { 10573 if (observer != null) { 10574 try { 10575 observer.backupFinished(status); 10576 } catch (RemoteException e) { 10577 if (DEBUG) { 10578 Slog.w(TAG, "Backup observer went away: backupFinished"); 10579 } 10580 } 10581 } 10582 } 10583} 10584