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