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