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