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