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