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