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