BackupManagerService.java revision 98f1ff05580f85debaa245addd02777fe9e68273
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 boolean restoreOneFile(InputStream instream, boolean mustKillAgent) { 5249 if (!isRunning()) { 5250 Slog.w(TAG, "Restore engine used after halting"); 5251 return false; 5252 } 5253 5254 FileMetadata info; 5255 try { 5256 if (MORE_DEBUG) { 5257 Slog.v(TAG, "Reading tar header for restoring file"); 5258 } 5259 info = readTarHeaders(instream); 5260 if (info != null) { 5261 if (MORE_DEBUG) { 5262 dumpFileMetadata(info); 5263 } 5264 5265 final String pkg = info.packageName; 5266 if (!pkg.equals(mAgentPackage)) { 5267 // In the single-package case, it's a semantic error to expect 5268 // one app's data but see a different app's on the wire 5269 if (mOnlyPackage != null) { 5270 if (!pkg.equals(mOnlyPackage.packageName)) { 5271 Slog.w(TAG, "Expected data for " + mOnlyPackage 5272 + " but saw " + pkg); 5273 setResult(RestoreEngine.TRANSPORT_FAILURE); 5274 setRunning(false); 5275 return false; 5276 } 5277 } 5278 5279 // okay, change in package; set up our various 5280 // bookkeeping if we haven't seen it yet 5281 if (!mPackagePolicies.containsKey(pkg)) { 5282 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5283 } 5284 5285 // Clean up the previous agent relationship if necessary, 5286 // and let the observer know we're considering a new app. 5287 if (mAgent != null) { 5288 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 5289 // Now we're really done 5290 tearDownPipes(); 5291 tearDownAgent(mTargetApp); 5292 mTargetApp = null; 5293 mAgentPackage = null; 5294 } 5295 } 5296 5297 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 5298 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 5299 mPackageInstallers.put(pkg, info.installerPackageName); 5300 // We've read only the manifest content itself at this point, 5301 // so consume the footer before looping around to the next 5302 // input file 5303 skipTarPadding(info.size, instream); 5304 sendOnRestorePackage(pkg); 5305 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 5306 // Metadata blobs! 5307 readMetadata(info, instream); 5308 skipTarPadding(info.size, instream); 5309 } else { 5310 // Non-manifest, so it's actual file data. Is this a package 5311 // we're ignoring? 5312 boolean okay = true; 5313 RestorePolicy policy = mPackagePolicies.get(pkg); 5314 switch (policy) { 5315 case IGNORE: 5316 okay = false; 5317 break; 5318 5319 case ACCEPT_IF_APK: 5320 // If we're in accept-if-apk state, then the first file we 5321 // see MUST be the apk. 5322 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5323 if (DEBUG) Slog.d(TAG, "APK file; installing"); 5324 // Try to install the app. 5325 String installerName = mPackageInstallers.get(pkg); 5326 okay = installApk(info, installerName, instream); 5327 // good to go; promote to ACCEPT 5328 mPackagePolicies.put(pkg, (okay) 5329 ? RestorePolicy.ACCEPT 5330 : RestorePolicy.IGNORE); 5331 // At this point we've consumed this file entry 5332 // ourselves, so just strip the tar footer and 5333 // go on to the next file in the input stream 5334 skipTarPadding(info.size, instream); 5335 return true; 5336 } else { 5337 // File data before (or without) the apk. We can't 5338 // handle it coherently in this case so ignore it. 5339 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5340 okay = false; 5341 } 5342 break; 5343 5344 case ACCEPT: 5345 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5346 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 5347 // we can take the data without the apk, so we 5348 // *want* to do so. skip the apk by declaring this 5349 // one file not-okay without changing the restore 5350 // policy for the package. 5351 okay = false; 5352 } 5353 break; 5354 5355 default: 5356 // Something has gone dreadfully wrong when determining 5357 // the restore policy from the manifest. Ignore the 5358 // rest of this package's data. 5359 Slog.e(TAG, "Invalid policy from manifest"); 5360 okay = false; 5361 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5362 break; 5363 } 5364 5365 // Is it a *file* we need to drop? 5366 if (!isRestorableFile(info)) { 5367 okay = false; 5368 } 5369 5370 // If the policy is satisfied, go ahead and set up to pipe the 5371 // data to the agent. 5372 if (DEBUG && okay && mAgent != null) { 5373 Slog.i(TAG, "Reusing existing agent instance"); 5374 } 5375 if (okay && mAgent == null) { 5376 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 5377 5378 try { 5379 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 5380 5381 // If we haven't sent any data to this app yet, we probably 5382 // need to clear it first. Check that. 5383 if (!mClearedPackages.contains(pkg)) { 5384 // apps with their own backup agents are 5385 // responsible for coherently managing a full 5386 // restore. 5387 if (mTargetApp.backupAgentName == null) { 5388 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 5389 clearApplicationDataSynchronous(pkg); 5390 } else { 5391 if (MORE_DEBUG) Slog.d(TAG, "backup agent (" 5392 + mTargetApp.backupAgentName + ") => no clear"); 5393 } 5394 mClearedPackages.add(pkg); 5395 } else { 5396 if (MORE_DEBUG) { 5397 Slog.d(TAG, "We've initialized this app already; no clear required"); 5398 } 5399 } 5400 5401 // All set; now set up the IPC and launch the agent 5402 setUpPipes(); 5403 mAgent = bindToAgentSynchronous(mTargetApp, 5404 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 5405 mAgentPackage = pkg; 5406 } catch (IOException e) { 5407 // fall through to error handling 5408 } catch (NameNotFoundException e) { 5409 // fall through to error handling 5410 } 5411 5412 if (mAgent == null) { 5413 Slog.e(TAG, "Unable to create agent for " + pkg); 5414 okay = false; 5415 tearDownPipes(); 5416 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5417 } 5418 } 5419 5420 // Sanity check: make sure we never give data to the wrong app. This 5421 // should never happen but a little paranoia here won't go amiss. 5422 if (okay && !pkg.equals(mAgentPackage)) { 5423 Slog.e(TAG, "Restoring data for " + pkg 5424 + " but agent is for " + mAgentPackage); 5425 okay = false; 5426 } 5427 5428 // At this point we have an agent ready to handle the full 5429 // restore data as well as a pipe for sending data to 5430 // that agent. Tell the agent to start reading from the 5431 // pipe. 5432 if (okay) { 5433 boolean agentSuccess = true; 5434 long toCopy = info.size; 5435 final int token = generateToken(); 5436 try { 5437 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 5438 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 5439 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 5440 + " : " + info.path); 5441 mObbConnection.restoreObbFile(pkg, mPipes[0], 5442 info.size, info.type, info.path, info.mode, 5443 info.mtime, token, mBackupManagerBinder); 5444 } else { 5445 if (MORE_DEBUG) Slog.d(TAG, "Invoking agent to restore file " 5446 + info.path); 5447 // fire up the app's agent listening on the socket. If 5448 // the agent is running in the system process we can't 5449 // just invoke it asynchronously, so we provide a thread 5450 // for it here. 5451 if (mTargetApp.processName.equals("system")) { 5452 Slog.d(TAG, "system process agent - spinning a thread"); 5453 RestoreFileRunnable runner = new RestoreFileRunnable( 5454 mAgent, info, mPipes[0], token); 5455 new Thread(runner, "restore-sys-runner").start(); 5456 } else { 5457 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 5458 info.domain, info.path, info.mode, info.mtime, 5459 token, mBackupManagerBinder); 5460 } 5461 } 5462 } catch (IOException e) { 5463 // couldn't dup the socket for a process-local restore 5464 Slog.d(TAG, "Couldn't establish restore"); 5465 agentSuccess = false; 5466 okay = false; 5467 } catch (RemoteException e) { 5468 // whoops, remote entity went away. We'll eat the content 5469 // ourselves, then, and not copy it over. 5470 Slog.e(TAG, "Agent crashed during full restore"); 5471 agentSuccess = false; 5472 okay = false; 5473 } 5474 5475 // Copy over the data if the agent is still good 5476 if (okay) { 5477 if (MORE_DEBUG) { 5478 Slog.v(TAG, " copying to restore agent: " 5479 + toCopy + " bytes"); 5480 } 5481 boolean pipeOkay = true; 5482 FileOutputStream pipe = new FileOutputStream( 5483 mPipes[1].getFileDescriptor()); 5484 while (toCopy > 0) { 5485 int toRead = (toCopy > mBuffer.length) 5486 ? mBuffer.length : (int)toCopy; 5487 int nRead = instream.read(mBuffer, 0, toRead); 5488 if (nRead >= 0) mBytes += nRead; 5489 if (nRead <= 0) break; 5490 toCopy -= nRead; 5491 5492 // send it to the output pipe as long as things 5493 // are still good 5494 if (pipeOkay) { 5495 try { 5496 pipe.write(mBuffer, 0, nRead); 5497 } catch (IOException e) { 5498 Slog.e(TAG, "Failed to write to restore pipe", e); 5499 pipeOkay = false; 5500 } 5501 } 5502 } 5503 5504 // done sending that file! Now we just need to consume 5505 // the delta from info.size to the end of block. 5506 skipTarPadding(info.size, instream); 5507 5508 // and now that we've sent it all, wait for the remote 5509 // side to acknowledge receipt 5510 agentSuccess = waitUntilOperationComplete(token); 5511 } 5512 5513 // okay, if the remote end failed at any point, deal with 5514 // it by ignoring the rest of the restore on it 5515 if (!agentSuccess) { 5516 Slog.w(TAG, "Agent failure; ending restore"); 5517 mBackupHandler.removeMessages(MSG_TIMEOUT); 5518 tearDownPipes(); 5519 tearDownAgent(mTargetApp); 5520 mAgent = null; 5521 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5522 5523 // If this was a single-package restore, we halt immediately 5524 // with an agent error under these circumstances 5525 if (mOnlyPackage != null) { 5526 setResult(RestoreEngine.TARGET_FAILURE); 5527 setRunning(false); 5528 return false; 5529 } 5530 } 5531 } 5532 5533 // Problems setting up the agent communication, an explicitly 5534 // dropped file, or an already-ignored package: skip to the 5535 // next stream entry by reading and discarding this file. 5536 if (!okay) { 5537 if (MORE_DEBUG) Slog.d(TAG, "[discarding file content]"); 5538 long bytesToConsume = (info.size + 511) & ~511; 5539 while (bytesToConsume > 0) { 5540 int toRead = (bytesToConsume > mBuffer.length) 5541 ? mBuffer.length : (int)bytesToConsume; 5542 long nRead = instream.read(mBuffer, 0, toRead); 5543 if (nRead >= 0) mBytes += nRead; 5544 if (nRead <= 0) break; 5545 bytesToConsume -= nRead; 5546 } 5547 } 5548 } 5549 } 5550 } catch (IOException e) { 5551 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 5552 setResult(RestoreEngine.TRANSPORT_FAILURE); 5553 info = null; 5554 } 5555 5556 // If we got here we're either running smoothly or we've finished 5557 if (info == null) { 5558 if (MORE_DEBUG) { 5559 Slog.i(TAG, "No [more] data for this package; tearing down"); 5560 } 5561 tearDownPipes(); 5562 setRunning(false); 5563 if (mustKillAgent) { 5564 tearDownAgent(mTargetApp); 5565 } 5566 } 5567 return (info != null); 5568 } 5569 5570 void setUpPipes() throws IOException { 5571 mPipes = ParcelFileDescriptor.createPipe(); 5572 } 5573 5574 void tearDownPipes() { 5575 if (mPipes != null) { 5576 try { 5577 mPipes[0].close(); 5578 mPipes[0] = null; 5579 mPipes[1].close(); 5580 mPipes[1] = null; 5581 } catch (IOException e) { 5582 Slog.w(TAG, "Couldn't close agent pipes", e); 5583 } 5584 mPipes = null; 5585 } 5586 } 5587 5588 void tearDownAgent(ApplicationInfo app) { 5589 if (mAgent != null) { 5590 tearDownAgentAndKill(app); 5591 mAgent = null; 5592 } 5593 } 5594 5595 class RestoreInstallObserver extends PackageInstallObserver { 5596 final AtomicBoolean mDone = new AtomicBoolean(); 5597 String mPackageName; 5598 int mResult; 5599 5600 public void reset() { 5601 synchronized (mDone) { 5602 mDone.set(false); 5603 } 5604 } 5605 5606 public void waitForCompletion() { 5607 synchronized (mDone) { 5608 while (mDone.get() == false) { 5609 try { 5610 mDone.wait(); 5611 } catch (InterruptedException e) { } 5612 } 5613 } 5614 } 5615 5616 int getResult() { 5617 return mResult; 5618 } 5619 5620 @Override 5621 public void onPackageInstalled(String packageName, int returnCode, 5622 String msg, Bundle extras) { 5623 synchronized (mDone) { 5624 mResult = returnCode; 5625 mPackageName = packageName; 5626 mDone.set(true); 5627 mDone.notifyAll(); 5628 } 5629 } 5630 } 5631 5632 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 5633 final AtomicBoolean mDone = new AtomicBoolean(); 5634 int mResult; 5635 5636 public void reset() { 5637 synchronized (mDone) { 5638 mDone.set(false); 5639 } 5640 } 5641 5642 public void waitForCompletion() { 5643 synchronized (mDone) { 5644 while (mDone.get() == false) { 5645 try { 5646 mDone.wait(); 5647 } catch (InterruptedException e) { } 5648 } 5649 } 5650 } 5651 5652 @Override 5653 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 5654 synchronized (mDone) { 5655 mResult = returnCode; 5656 mDone.set(true); 5657 mDone.notifyAll(); 5658 } 5659 } 5660 } 5661 5662 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 5663 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 5664 5665 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 5666 boolean okay = true; 5667 5668 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 5669 5670 // The file content is an .apk file. Copy it out to a staging location and 5671 // attempt to install it. 5672 File apkFile = new File(mDataDir, info.packageName); 5673 try { 5674 FileOutputStream apkStream = new FileOutputStream(apkFile); 5675 byte[] buffer = new byte[32 * 1024]; 5676 long size = info.size; 5677 while (size > 0) { 5678 long toRead = (buffer.length < size) ? buffer.length : size; 5679 int didRead = instream.read(buffer, 0, (int)toRead); 5680 if (didRead >= 0) mBytes += didRead; 5681 apkStream.write(buffer, 0, didRead); 5682 size -= didRead; 5683 } 5684 apkStream.close(); 5685 5686 // make sure the installer can read it 5687 apkFile.setReadable(true, false); 5688 5689 // Now install it 5690 Uri packageUri = Uri.fromFile(apkFile); 5691 mInstallObserver.reset(); 5692 mPackageManager.installPackage(packageUri, mInstallObserver, 5693 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 5694 installerPackage); 5695 mInstallObserver.waitForCompletion(); 5696 5697 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 5698 // The only time we continue to accept install of data even if the 5699 // apk install failed is if we had already determined that we could 5700 // accept the data regardless. 5701 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 5702 okay = false; 5703 } 5704 } else { 5705 // Okay, the install succeeded. Make sure it was the right app. 5706 boolean uninstall = false; 5707 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 5708 Slog.w(TAG, "Restore stream claimed to include apk for " 5709 + info.packageName + " but apk was really " 5710 + mInstallObserver.mPackageName); 5711 // delete the package we just put in place; it might be fraudulent 5712 okay = false; 5713 uninstall = true; 5714 } else { 5715 try { 5716 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 5717 PackageManager.GET_SIGNATURES); 5718 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 5719 Slog.w(TAG, "Restore stream contains apk of package " 5720 + info.packageName + " but it disallows backup/restore"); 5721 okay = false; 5722 } else { 5723 // So far so good -- do the signatures match the manifest? 5724 Signature[] sigs = mManifestSignatures.get(info.packageName); 5725 if (signaturesMatch(sigs, pkg)) { 5726 // If this is a system-uid app without a declared backup agent, 5727 // don't restore any of the file data. 5728 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 5729 && (pkg.applicationInfo.backupAgentName == null)) { 5730 Slog.w(TAG, "Installed app " + info.packageName 5731 + " has restricted uid and no agent"); 5732 okay = false; 5733 } 5734 } else { 5735 Slog.w(TAG, "Installed app " + info.packageName 5736 + " signatures do not match restore manifest"); 5737 okay = false; 5738 uninstall = true; 5739 } 5740 } 5741 } catch (NameNotFoundException e) { 5742 Slog.w(TAG, "Install of package " + info.packageName 5743 + " succeeded but now not found"); 5744 okay = false; 5745 } 5746 } 5747 5748 // If we're not okay at this point, we need to delete the package 5749 // that we just installed. 5750 if (uninstall) { 5751 mDeleteObserver.reset(); 5752 mPackageManager.deletePackage(mInstallObserver.mPackageName, 5753 mDeleteObserver, 0); 5754 mDeleteObserver.waitForCompletion(); 5755 } 5756 } 5757 } catch (IOException e) { 5758 Slog.e(TAG, "Unable to transcribe restored apk for install"); 5759 okay = false; 5760 } finally { 5761 apkFile.delete(); 5762 } 5763 5764 return okay; 5765 } 5766 5767 // Given an actual file content size, consume the post-content padding mandated 5768 // by the tar format. 5769 void skipTarPadding(long size, InputStream instream) throws IOException { 5770 long partial = (size + 512) % 512; 5771 if (partial > 0) { 5772 final int needed = 512 - (int)partial; 5773 if (MORE_DEBUG) { 5774 Slog.i(TAG, "Skipping tar padding: " + needed + " bytes"); 5775 } 5776 byte[] buffer = new byte[needed]; 5777 if (readExactly(instream, buffer, 0, needed) == needed) { 5778 mBytes += needed; 5779 } else throw new IOException("Unexpected EOF in padding"); 5780 } 5781 } 5782 5783 // Read a widget metadata file, returning the restored blob 5784 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 5785 // Fail on suspiciously large widget dump files 5786 if (info.size > 64 * 1024) { 5787 throw new IOException("Metadata too big; corrupt? size=" + info.size); 5788 } 5789 5790 byte[] buffer = new byte[(int) info.size]; 5791 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5792 mBytes += info.size; 5793 } else throw new IOException("Unexpected EOF in widget data"); 5794 5795 String[] str = new String[1]; 5796 int offset = extractLine(buffer, 0, str); 5797 int version = Integer.parseInt(str[0]); 5798 if (version == BACKUP_MANIFEST_VERSION) { 5799 offset = extractLine(buffer, offset, str); 5800 final String pkg = str[0]; 5801 if (info.packageName.equals(pkg)) { 5802 // Data checks out -- the rest of the buffer is a concatenation of 5803 // binary blobs as described in the comment at writeAppWidgetData() 5804 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 5805 offset, buffer.length - offset); 5806 DataInputStream in = new DataInputStream(bin); 5807 while (bin.available() > 0) { 5808 int token = in.readInt(); 5809 int size = in.readInt(); 5810 if (size > 64 * 1024) { 5811 throw new IOException("Datum " 5812 + Integer.toHexString(token) 5813 + " too big; corrupt? size=" + info.size); 5814 } 5815 switch (token) { 5816 case BACKUP_WIDGET_METADATA_TOKEN: 5817 { 5818 if (MORE_DEBUG) { 5819 Slog.i(TAG, "Got widget metadata for " + info.packageName); 5820 } 5821 mWidgetData = new byte[size]; 5822 in.read(mWidgetData); 5823 break; 5824 } 5825 default: 5826 { 5827 if (DEBUG) { 5828 Slog.i(TAG, "Ignoring metadata blob " 5829 + Integer.toHexString(token) 5830 + " for " + info.packageName); 5831 } 5832 in.skipBytes(size); 5833 break; 5834 } 5835 } 5836 } 5837 } else { 5838 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 5839 + " but widget data for " + pkg); 5840 } 5841 } else { 5842 Slog.w(TAG, "Unsupported metadata version " + version); 5843 } 5844 } 5845 5846 // Returns a policy constant 5847 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 5848 throws IOException { 5849 // Fail on suspiciously large manifest files 5850 if (info.size > 64 * 1024) { 5851 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 5852 } 5853 5854 byte[] buffer = new byte[(int) info.size]; 5855 if (MORE_DEBUG) { 5856 Slog.i(TAG, " readAppManifest() looking for " + info.size + " bytes, " 5857 + mBytes + " already consumed"); 5858 } 5859 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5860 mBytes += info.size; 5861 } else throw new IOException("Unexpected EOF in manifest"); 5862 5863 RestorePolicy policy = RestorePolicy.IGNORE; 5864 String[] str = new String[1]; 5865 int offset = 0; 5866 5867 try { 5868 offset = extractLine(buffer, offset, str); 5869 int version = Integer.parseInt(str[0]); 5870 if (version == BACKUP_MANIFEST_VERSION) { 5871 offset = extractLine(buffer, offset, str); 5872 String manifestPackage = str[0]; 5873 // TODO: handle <original-package> 5874 if (manifestPackage.equals(info.packageName)) { 5875 offset = extractLine(buffer, offset, str); 5876 version = Integer.parseInt(str[0]); // app version 5877 offset = extractLine(buffer, offset, str); 5878 // This is the platform version, which we don't use, but we parse it 5879 // as a safety against corruption in the manifest. 5880 Integer.parseInt(str[0]); 5881 offset = extractLine(buffer, offset, str); 5882 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 5883 offset = extractLine(buffer, offset, str); 5884 boolean hasApk = str[0].equals("1"); 5885 offset = extractLine(buffer, offset, str); 5886 int numSigs = Integer.parseInt(str[0]); 5887 if (numSigs > 0) { 5888 Signature[] sigs = new Signature[numSigs]; 5889 for (int i = 0; i < numSigs; i++) { 5890 offset = extractLine(buffer, offset, str); 5891 sigs[i] = new Signature(str[0]); 5892 } 5893 mManifestSignatures.put(info.packageName, sigs); 5894 5895 // Okay, got the manifest info we need... 5896 try { 5897 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 5898 info.packageName, PackageManager.GET_SIGNATURES); 5899 // Fall through to IGNORE if the app explicitly disallows backup 5900 final int flags = pkgInfo.applicationInfo.flags; 5901 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 5902 // Restore system-uid-space packages only if they have 5903 // defined a custom backup agent 5904 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 5905 || (pkgInfo.applicationInfo.backupAgentName != null)) { 5906 // Verify signatures against any installed version; if they 5907 // don't match, then we fall though and ignore the data. The 5908 // signatureMatch() method explicitly ignores the signature 5909 // check for packages installed on the system partition, because 5910 // such packages are signed with the platform cert instead of 5911 // the app developer's cert, so they're different on every 5912 // device. 5913 if (signaturesMatch(sigs, pkgInfo)) { 5914 if (pkgInfo.versionCode >= version) { 5915 Slog.i(TAG, "Sig + version match; taking data"); 5916 policy = RestorePolicy.ACCEPT; 5917 } else { 5918 // The data is from a newer version of the app than 5919 // is presently installed. That means we can only 5920 // use it if the matching apk is also supplied. 5921 if (mAllowApks) { 5922 Slog.i(TAG, "Data version " + version 5923 + " is newer than installed version " 5924 + pkgInfo.versionCode 5925 + " - requiring apk"); 5926 policy = RestorePolicy.ACCEPT_IF_APK; 5927 } else { 5928 Slog.i(TAG, "Data requires newer version " 5929 + version + "; ignoring"); 5930 policy = RestorePolicy.IGNORE; 5931 } 5932 } 5933 } else { 5934 Slog.w(TAG, "Restore manifest signatures do not match " 5935 + "installed application for " + info.packageName); 5936 } 5937 } else { 5938 Slog.w(TAG, "Package " + info.packageName 5939 + " is system level with no agent"); 5940 } 5941 } else { 5942 if (DEBUG) Slog.i(TAG, "Restore manifest from " 5943 + info.packageName + " but allowBackup=false"); 5944 } 5945 } catch (NameNotFoundException e) { 5946 // Okay, the target app isn't installed. We can process 5947 // the restore properly only if the dataset provides the 5948 // apk file and we can successfully install it. 5949 if (mAllowApks) { 5950 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 5951 + " not installed; requiring apk in dataset"); 5952 policy = RestorePolicy.ACCEPT_IF_APK; 5953 } else { 5954 policy = RestorePolicy.IGNORE; 5955 } 5956 } 5957 5958 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 5959 Slog.i(TAG, "Cannot restore package " + info.packageName 5960 + " without the matching .apk"); 5961 } 5962 } else { 5963 Slog.i(TAG, "Missing signature on backed-up package " 5964 + info.packageName); 5965 } 5966 } else { 5967 Slog.i(TAG, "Expected package " + info.packageName 5968 + " but restore manifest claims " + manifestPackage); 5969 } 5970 } else { 5971 Slog.i(TAG, "Unknown restore manifest version " + version 5972 + " for package " + info.packageName); 5973 } 5974 } catch (NumberFormatException e) { 5975 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 5976 } catch (IllegalArgumentException e) { 5977 Slog.w(TAG, e.getMessage()); 5978 } 5979 5980 return policy; 5981 } 5982 5983 // Builds a line from a byte buffer starting at 'offset', and returns 5984 // the index of the next unconsumed data in the buffer. 5985 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 5986 final int end = buffer.length; 5987 if (offset >= end) throw new IOException("Incomplete data"); 5988 5989 int pos; 5990 for (pos = offset; pos < end; pos++) { 5991 byte c = buffer[pos]; 5992 // at LF we declare end of line, and return the next char as the 5993 // starting point for the next time through 5994 if (c == '\n') { 5995 break; 5996 } 5997 } 5998 outStr[0] = new String(buffer, offset, pos - offset); 5999 pos++; // may be pointing an extra byte past the end but that's okay 6000 return pos; 6001 } 6002 6003 void dumpFileMetadata(FileMetadata info) { 6004 if (MORE_DEBUG) { 6005 StringBuilder b = new StringBuilder(128); 6006 6007 // mode string 6008 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 6009 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 6010 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 6011 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 6012 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 6013 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 6014 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 6015 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 6016 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 6017 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 6018 b.append(String.format(" %9d ", info.size)); 6019 6020 Date stamp = new Date(info.mtime); 6021 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 6022 6023 b.append(info.packageName); 6024 b.append(" :: "); 6025 b.append(info.domain); 6026 b.append(" :: "); 6027 b.append(info.path); 6028 6029 Slog.i(TAG, b.toString()); 6030 } 6031 } 6032 6033 // Consume a tar file header block [sequence] and accumulate the relevant metadata 6034 FileMetadata readTarHeaders(InputStream instream) throws IOException { 6035 byte[] block = new byte[512]; 6036 FileMetadata info = null; 6037 6038 boolean gotHeader = readTarHeader(instream, block); 6039 if (gotHeader) { 6040 try { 6041 // okay, presume we're okay, and extract the various metadata 6042 info = new FileMetadata(); 6043 info.size = extractRadix(block, 124, 12, 8); 6044 info.mtime = extractRadix(block, 136, 12, 8); 6045 info.mode = extractRadix(block, 100, 8, 8); 6046 6047 info.path = extractString(block, 345, 155); // prefix 6048 String path = extractString(block, 0, 100); 6049 if (path.length() > 0) { 6050 if (info.path.length() > 0) info.path += '/'; 6051 info.path += path; 6052 } 6053 6054 // tar link indicator field: 1 byte at offset 156 in the header. 6055 int typeChar = block[156]; 6056 if (typeChar == 'x') { 6057 // pax extended header, so we need to read that 6058 gotHeader = readPaxExtendedHeader(instream, info); 6059 if (gotHeader) { 6060 // and after a pax extended header comes another real header -- read 6061 // that to find the real file type 6062 gotHeader = readTarHeader(instream, block); 6063 } 6064 if (!gotHeader) throw new IOException("Bad or missing pax header"); 6065 6066 typeChar = block[156]; 6067 } 6068 6069 switch (typeChar) { 6070 case '0': info.type = BackupAgent.TYPE_FILE; break; 6071 case '5': { 6072 info.type = BackupAgent.TYPE_DIRECTORY; 6073 if (info.size != 0) { 6074 Slog.w(TAG, "Directory entry with nonzero size in header"); 6075 info.size = 0; 6076 } 6077 break; 6078 } 6079 case 0: { 6080 // presume EOF 6081 if (MORE_DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 6082 return null; 6083 } 6084 default: { 6085 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 6086 throw new IOException("Unknown entity type " + typeChar); 6087 } 6088 } 6089 6090 // Parse out the path 6091 // 6092 // first: apps/shared/unrecognized 6093 if (FullBackup.SHARED_PREFIX.regionMatches(0, 6094 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 6095 // File in shared storage. !!! TODO: implement this. 6096 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 6097 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 6098 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 6099 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 6100 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 6101 info.path, 0, FullBackup.APPS_PREFIX.length())) { 6102 // App content! Parse out the package name and domain 6103 6104 // strip the apps/ prefix 6105 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 6106 6107 // extract the package name 6108 int slash = info.path.indexOf('/'); 6109 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 6110 info.packageName = info.path.substring(0, slash); 6111 info.path = info.path.substring(slash+1); 6112 6113 // if it's a manifest or metadata payload we're done, otherwise parse 6114 // out the domain into which the file will be restored 6115 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 6116 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 6117 slash = info.path.indexOf('/'); 6118 if (slash < 0) { 6119 throw new IOException("Illegal semantic path in non-manifest " 6120 + info.path); 6121 } 6122 info.domain = info.path.substring(0, slash); 6123 info.path = info.path.substring(slash + 1); 6124 } 6125 } 6126 } catch (IOException e) { 6127 if (DEBUG) { 6128 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 6129 if (MORE_DEBUG) { 6130 HEXLOG(block); 6131 } 6132 } 6133 throw e; 6134 } 6135 } 6136 return info; 6137 } 6138 6139 private boolean isRestorableFile(FileMetadata info) { 6140 if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { 6141 if (MORE_DEBUG) { 6142 Slog.i(TAG, "Dropping cache file path " + info.path); 6143 } 6144 return false; 6145 } 6146 6147 if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) { 6148 // It's possible this is "no-backup" dir contents in an archive stream 6149 // produced on a device running a version of the OS that predates that 6150 // API. Respect the no-backup intention and don't let the data get to 6151 // the app. 6152 if (info.path.startsWith("no_backup/")) { 6153 if (MORE_DEBUG) { 6154 Slog.i(TAG, "Dropping no_backup file path " + info.path); 6155 } 6156 return false; 6157 } 6158 } 6159 6160 // The path needs to be canonical 6161 if (info.path.contains("..") || info.path.contains("//")) { 6162 if (MORE_DEBUG) { 6163 Slog.w(TAG, "Dropping invalid path " + info.path); 6164 } 6165 return false; 6166 } 6167 6168 // Otherwise we think this file is good to go 6169 return true; 6170 } 6171 6172 private void HEXLOG(byte[] block) { 6173 int offset = 0; 6174 int todo = block.length; 6175 StringBuilder buf = new StringBuilder(64); 6176 while (todo > 0) { 6177 buf.append(String.format("%04x ", offset)); 6178 int numThisLine = (todo > 16) ? 16 : todo; 6179 for (int i = 0; i < numThisLine; i++) { 6180 buf.append(String.format("%02x ", block[offset+i])); 6181 } 6182 Slog.i("hexdump", buf.toString()); 6183 buf.setLength(0); 6184 todo -= numThisLine; 6185 offset += numThisLine; 6186 } 6187 } 6188 6189 // Read exactly the given number of bytes into a buffer at the stated offset. 6190 // Returns false if EOF is encountered before the requested number of bytes 6191 // could be read. 6192 int readExactly(InputStream in, byte[] buffer, int offset, int size) 6193 throws IOException { 6194 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 6195if (MORE_DEBUG) Slog.i(TAG, " ... readExactly(" + size + ") called"); 6196 int soFar = 0; 6197 while (soFar < size) { 6198 int nRead = in.read(buffer, offset + soFar, size - soFar); 6199 if (nRead <= 0) { 6200 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 6201 break; 6202 } 6203 soFar += nRead; 6204if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar)); 6205 } 6206 return soFar; 6207 } 6208 6209 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 6210 final int got = readExactly(instream, block, 0, 512); 6211 if (got == 0) return false; // Clean EOF 6212 if (got < 512) throw new IOException("Unable to read full block header"); 6213 mBytes += 512; 6214 return true; 6215 } 6216 6217 // overwrites 'info' fields based on the pax extended header 6218 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 6219 throws IOException { 6220 // We should never see a pax extended header larger than this 6221 if (info.size > 32*1024) { 6222 Slog.w(TAG, "Suspiciously large pax header size " + info.size 6223 + " - aborting"); 6224 throw new IOException("Sanity failure: pax header size " + info.size); 6225 } 6226 6227 // read whole blocks, not just the content size 6228 int numBlocks = (int)((info.size + 511) >> 9); 6229 byte[] data = new byte[numBlocks * 512]; 6230 if (readExactly(instream, data, 0, data.length) < data.length) { 6231 throw new IOException("Unable to read full pax header"); 6232 } 6233 mBytes += data.length; 6234 6235 final int contentSize = (int) info.size; 6236 int offset = 0; 6237 do { 6238 // extract the line at 'offset' 6239 int eol = offset+1; 6240 while (eol < contentSize && data[eol] != ' ') eol++; 6241 if (eol >= contentSize) { 6242 // error: we just hit EOD looking for the end of the size field 6243 throw new IOException("Invalid pax data"); 6244 } 6245 // eol points to the space between the count and the key 6246 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 6247 int key = eol + 1; // start of key=value 6248 eol = offset + linelen - 1; // trailing LF 6249 int value; 6250 for (value = key+1; data[value] != '=' && value <= eol; value++); 6251 if (value > eol) { 6252 throw new IOException("Invalid pax declaration"); 6253 } 6254 6255 // pax requires that key/value strings be in UTF-8 6256 String keyStr = new String(data, key, value-key, "UTF-8"); 6257 // -1 to strip the trailing LF 6258 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 6259 6260 if ("path".equals(keyStr)) { 6261 info.path = valStr; 6262 } else if ("size".equals(keyStr)) { 6263 info.size = Long.parseLong(valStr); 6264 } else { 6265 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 6266 } 6267 6268 offset += linelen; 6269 } while (offset < contentSize); 6270 6271 return true; 6272 } 6273 6274 long extractRadix(byte[] data, int offset, int maxChars, int radix) 6275 throws IOException { 6276 long value = 0; 6277 final int end = offset + maxChars; 6278 for (int i = offset; i < end; i++) { 6279 final byte b = data[i]; 6280 // Numeric fields in tar can terminate with either NUL or SPC 6281 if (b == 0 || b == ' ') break; 6282 if (b < '0' || b > ('0' + radix - 1)) { 6283 throw new IOException("Invalid number in header: '" + (char)b 6284 + "' for radix " + radix); 6285 } 6286 value = radix * value + (b - '0'); 6287 } 6288 return value; 6289 } 6290 6291 String extractString(byte[] data, int offset, int maxChars) throws IOException { 6292 final int end = offset + maxChars; 6293 int eos = offset; 6294 // tar string fields terminate early with a NUL 6295 while (eos < end && data[eos] != 0) eos++; 6296 return new String(data, offset, eos-offset, "US-ASCII"); 6297 } 6298 6299 void sendStartRestore() { 6300 if (mObserver != null) { 6301 try { 6302 mObserver.onStartRestore(); 6303 } catch (RemoteException e) { 6304 Slog.w(TAG, "full restore observer went away: startRestore"); 6305 mObserver = null; 6306 } 6307 } 6308 } 6309 6310 void sendOnRestorePackage(String name) { 6311 if (mObserver != null) { 6312 try { 6313 // TODO: use a more user-friendly name string 6314 mObserver.onRestorePackage(name); 6315 } catch (RemoteException e) { 6316 Slog.w(TAG, "full restore observer went away: restorePackage"); 6317 mObserver = null; 6318 } 6319 } 6320 } 6321 6322 void sendEndRestore() { 6323 if (mObserver != null) { 6324 try { 6325 mObserver.onEndRestore(); 6326 } catch (RemoteException e) { 6327 Slog.w(TAG, "full restore observer went away: endRestore"); 6328 mObserver = null; 6329 } 6330 } 6331 } 6332 } 6333 6334 // ***** end new engine class *** 6335 6336 class PerformAdbRestoreTask implements Runnable { 6337 ParcelFileDescriptor mInputFile; 6338 String mCurrentPassword; 6339 String mDecryptPassword; 6340 IFullBackupRestoreObserver mObserver; 6341 AtomicBoolean mLatchObject; 6342 IBackupAgent mAgent; 6343 String mAgentPackage; 6344 ApplicationInfo mTargetApp; 6345 FullBackupObbConnection mObbConnection = null; 6346 ParcelFileDescriptor[] mPipes = null; 6347 byte[] mWidgetData = null; 6348 6349 long mBytes; 6350 6351 // possible handling states for a given package in the restore dataset 6352 final HashMap<String, RestorePolicy> mPackagePolicies 6353 = new HashMap<String, RestorePolicy>(); 6354 6355 // installer package names for each encountered app, derived from the manifests 6356 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 6357 6358 // Signatures for a given package found in its manifest file 6359 final HashMap<String, Signature[]> mManifestSignatures 6360 = new HashMap<String, Signature[]>(); 6361 6362 // Packages we've already wiped data on when restoring their first file 6363 final HashSet<String> mClearedPackages = new HashSet<String>(); 6364 6365 PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword, 6366 IFullBackupRestoreObserver observer, AtomicBoolean latch) { 6367 mInputFile = fd; 6368 mCurrentPassword = curPassword; 6369 mDecryptPassword = decryptPassword; 6370 mObserver = observer; 6371 mLatchObject = latch; 6372 mAgent = null; 6373 mAgentPackage = null; 6374 mTargetApp = null; 6375 mObbConnection = new FullBackupObbConnection(); 6376 6377 // Which packages we've already wiped data on. We prepopulate this 6378 // with a whitelist of packages known to be unclearable. 6379 mClearedPackages.add("android"); 6380 mClearedPackages.add(SETTINGS_PACKAGE); 6381 } 6382 6383 class RestoreFileRunnable implements Runnable { 6384 IBackupAgent mAgent; 6385 FileMetadata mInfo; 6386 ParcelFileDescriptor mSocket; 6387 int mToken; 6388 6389 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 6390 ParcelFileDescriptor socket, int token) throws IOException { 6391 mAgent = agent; 6392 mInfo = info; 6393 mToken = token; 6394 6395 // This class is used strictly for process-local binder invocations. The 6396 // semantics of ParcelFileDescriptor differ in this case; in particular, we 6397 // do not automatically get a 'dup'ed descriptor that we can can continue 6398 // to use asynchronously from the caller. So, we make sure to dup it ourselves 6399 // before proceeding to do the restore. 6400 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 6401 } 6402 6403 @Override 6404 public void run() { 6405 try { 6406 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 6407 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 6408 mToken, mBackupManagerBinder); 6409 } catch (RemoteException e) { 6410 // never happens; this is used strictly for local binder calls 6411 } 6412 } 6413 } 6414 6415 @Override 6416 public void run() { 6417 Slog.i(TAG, "--- Performing full-dataset restore ---"); 6418 mObbConnection.establish(); 6419 sendStartRestore(); 6420 6421 // Are we able to restore shared-storage data? 6422 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 6423 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT); 6424 } 6425 6426 FileInputStream rawInStream = null; 6427 DataInputStream rawDataIn = null; 6428 try { 6429 if (!backupPasswordMatches(mCurrentPassword)) { 6430 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 6431 return; 6432 } 6433 6434 mBytes = 0; 6435 byte[] buffer = new byte[32 * 1024]; 6436 rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); 6437 rawDataIn = new DataInputStream(rawInStream); 6438 6439 // First, parse out the unencrypted/uncompressed header 6440 boolean compressed = false; 6441 InputStream preCompressStream = rawInStream; 6442 final InputStream in; 6443 6444 boolean okay = false; 6445 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length(); 6446 byte[] streamHeader = new byte[headerLen]; 6447 rawDataIn.readFully(streamHeader); 6448 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8"); 6449 if (Arrays.equals(magicBytes, streamHeader)) { 6450 // okay, header looks good. now parse out the rest of the fields. 6451 String s = readHeaderLine(rawInStream); 6452 final int archiveVersion = Integer.parseInt(s); 6453 if (archiveVersion <= BACKUP_FILE_VERSION) { 6454 // okay, it's a version we recognize. if it's version 1, we may need 6455 // to try two different PBKDF2 regimes to compare checksums. 6456 final boolean pbkdf2Fallback = (archiveVersion == 1); 6457 6458 s = readHeaderLine(rawInStream); 6459 compressed = (Integer.parseInt(s) != 0); 6460 s = readHeaderLine(rawInStream); 6461 if (s.equals("none")) { 6462 // no more header to parse; we're good to go 6463 okay = true; 6464 } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) { 6465 preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback, 6466 rawInStream); 6467 if (preCompressStream != null) { 6468 okay = true; 6469 } 6470 } else Slog.w(TAG, "Archive is encrypted but no password given"); 6471 } else Slog.w(TAG, "Wrong header version: " + s); 6472 } else Slog.w(TAG, "Didn't read the right header magic"); 6473 6474 if (!okay) { 6475 Slog.w(TAG, "Invalid restore data; aborting."); 6476 return; 6477 } 6478 6479 // okay, use the right stream layer based on compression 6480 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream; 6481 6482 boolean didRestore; 6483 do { 6484 didRestore = restoreOneFile(in, buffer); 6485 } while (didRestore); 6486 6487 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); 6488 } catch (IOException e) { 6489 Slog.e(TAG, "Unable to read restore input"); 6490 } finally { 6491 tearDownPipes(); 6492 tearDownAgent(mTargetApp); 6493 6494 try { 6495 if (rawDataIn != null) rawDataIn.close(); 6496 if (rawInStream != null) rawInStream.close(); 6497 mInputFile.close(); 6498 } catch (IOException e) { 6499 Slog.w(TAG, "Close of restore data pipe threw", e); 6500 /* nothing we can do about this */ 6501 } 6502 synchronized (mCurrentOpLock) { 6503 mCurrentOperations.clear(); 6504 } 6505 synchronized (mLatchObject) { 6506 mLatchObject.set(true); 6507 mLatchObject.notifyAll(); 6508 } 6509 mObbConnection.tearDown(); 6510 sendEndRestore(); 6511 Slog.d(TAG, "Full restore pass complete."); 6512 mWakelock.release(); 6513 } 6514 } 6515 6516 String readHeaderLine(InputStream in) throws IOException { 6517 int c; 6518 StringBuilder buffer = new StringBuilder(80); 6519 while ((c = in.read()) >= 0) { 6520 if (c == '\n') break; // consume and discard the newlines 6521 buffer.append((char)c); 6522 } 6523 return buffer.toString(); 6524 } 6525 6526 InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt, 6527 int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream, 6528 boolean doLog) { 6529 InputStream result = null; 6530 6531 try { 6532 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 6533 SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt, 6534 rounds); 6535 byte[] IV = hexToByteArray(userIvHex); 6536 IvParameterSpec ivSpec = new IvParameterSpec(IV); 6537 c.init(Cipher.DECRYPT_MODE, 6538 new SecretKeySpec(userKey.getEncoded(), "AES"), 6539 ivSpec); 6540 byte[] mkCipher = hexToByteArray(masterKeyBlobHex); 6541 byte[] mkBlob = c.doFinal(mkCipher); 6542 6543 // first, the master key IV 6544 int offset = 0; 6545 int len = mkBlob[offset++]; 6546 IV = Arrays.copyOfRange(mkBlob, offset, offset + len); 6547 offset += len; 6548 // then the master key itself 6549 len = mkBlob[offset++]; 6550 byte[] mk = Arrays.copyOfRange(mkBlob, 6551 offset, offset + len); 6552 offset += len; 6553 // and finally the master key checksum hash 6554 len = mkBlob[offset++]; 6555 byte[] mkChecksum = Arrays.copyOfRange(mkBlob, 6556 offset, offset + len); 6557 6558 // now validate the decrypted master key against the checksum 6559 byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds); 6560 if (Arrays.equals(calculatedCk, mkChecksum)) { 6561 ivSpec = new IvParameterSpec(IV); 6562 c.init(Cipher.DECRYPT_MODE, 6563 new SecretKeySpec(mk, "AES"), 6564 ivSpec); 6565 // Only if all of the above worked properly will 'result' be assigned 6566 result = new CipherInputStream(rawInStream, c); 6567 } else if (doLog) Slog.w(TAG, "Incorrect password"); 6568 } catch (InvalidAlgorithmParameterException e) { 6569 if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e); 6570 } catch (BadPaddingException e) { 6571 // This case frequently occurs when the wrong password is used to decrypt 6572 // the master key. Use the identical "incorrect password" log text as is 6573 // used in the checksum failure log in order to avoid providing additional 6574 // information to an attacker. 6575 if (doLog) Slog.w(TAG, "Incorrect password"); 6576 } catch (IllegalBlockSizeException e) { 6577 if (doLog) Slog.w(TAG, "Invalid block size in master key"); 6578 } catch (NoSuchAlgorithmException e) { 6579 if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!"); 6580 } catch (NoSuchPaddingException e) { 6581 if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!"); 6582 } catch (InvalidKeyException e) { 6583 if (doLog) Slog.w(TAG, "Illegal password; aborting"); 6584 } 6585 6586 return result; 6587 } 6588 6589 InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback, 6590 InputStream rawInStream) { 6591 InputStream result = null; 6592 try { 6593 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) { 6594 6595 String userSaltHex = readHeaderLine(rawInStream); // 5 6596 byte[] userSalt = hexToByteArray(userSaltHex); 6597 6598 String ckSaltHex = readHeaderLine(rawInStream); // 6 6599 byte[] ckSalt = hexToByteArray(ckSaltHex); 6600 6601 int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 6602 String userIvHex = readHeaderLine(rawInStream); // 8 6603 6604 String masterKeyBlobHex = readHeaderLine(rawInStream); // 9 6605 6606 // decrypt the master key blob 6607 result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt, 6608 rounds, userIvHex, masterKeyBlobHex, rawInStream, false); 6609 if (result == null && pbkdf2Fallback) { 6610 result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt, 6611 rounds, userIvHex, masterKeyBlobHex, rawInStream, true); 6612 } 6613 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName); 6614 } catch (NumberFormatException e) { 6615 Slog.w(TAG, "Can't parse restore data header"); 6616 } catch (IOException e) { 6617 Slog.w(TAG, "Can't read input header"); 6618 } 6619 6620 return result; 6621 } 6622 6623 boolean restoreOneFile(InputStream instream, byte[] buffer) { 6624 FileMetadata info; 6625 try { 6626 info = readTarHeaders(instream); 6627 if (info != null) { 6628 if (MORE_DEBUG) { 6629 dumpFileMetadata(info); 6630 } 6631 6632 final String pkg = info.packageName; 6633 if (!pkg.equals(mAgentPackage)) { 6634 // okay, change in package; set up our various 6635 // bookkeeping if we haven't seen it yet 6636 if (!mPackagePolicies.containsKey(pkg)) { 6637 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6638 } 6639 6640 // Clean up the previous agent relationship if necessary, 6641 // and let the observer know we're considering a new app. 6642 if (mAgent != null) { 6643 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 6644 // Now we're really done 6645 tearDownPipes(); 6646 tearDownAgent(mTargetApp); 6647 mTargetApp = null; 6648 mAgentPackage = null; 6649 } 6650 } 6651 6652 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 6653 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 6654 mPackageInstallers.put(pkg, info.installerPackageName); 6655 // We've read only the manifest content itself at this point, 6656 // so consume the footer before looping around to the next 6657 // input file 6658 skipTarPadding(info.size, instream); 6659 sendOnRestorePackage(pkg); 6660 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 6661 // Metadata blobs! 6662 readMetadata(info, instream); 6663 skipTarPadding(info.size, instream); 6664 } else { 6665 // Non-manifest, so it's actual file data. Is this a package 6666 // we're ignoring? 6667 boolean okay = true; 6668 RestorePolicy policy = mPackagePolicies.get(pkg); 6669 switch (policy) { 6670 case IGNORE: 6671 okay = false; 6672 break; 6673 6674 case ACCEPT_IF_APK: 6675 // If we're in accept-if-apk state, then the first file we 6676 // see MUST be the apk. 6677 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 6678 if (DEBUG) Slog.d(TAG, "APK file; installing"); 6679 // Try to install the app. 6680 String installerName = mPackageInstallers.get(pkg); 6681 okay = installApk(info, installerName, instream); 6682 // good to go; promote to ACCEPT 6683 mPackagePolicies.put(pkg, (okay) 6684 ? RestorePolicy.ACCEPT 6685 : RestorePolicy.IGNORE); 6686 // At this point we've consumed this file entry 6687 // ourselves, so just strip the tar footer and 6688 // go on to the next file in the input stream 6689 skipTarPadding(info.size, instream); 6690 return true; 6691 } else { 6692 // File data before (or without) the apk. We can't 6693 // handle it coherently in this case so ignore it. 6694 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6695 okay = false; 6696 } 6697 break; 6698 6699 case ACCEPT: 6700 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 6701 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 6702 // we can take the data without the apk, so we 6703 // *want* to do so. skip the apk by declaring this 6704 // one file not-okay without changing the restore 6705 // policy for the package. 6706 okay = false; 6707 } 6708 break; 6709 6710 default: 6711 // Something has gone dreadfully wrong when determining 6712 // the restore policy from the manifest. Ignore the 6713 // rest of this package's data. 6714 Slog.e(TAG, "Invalid policy from manifest"); 6715 okay = false; 6716 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6717 break; 6718 } 6719 6720 // The path needs to be canonical 6721 if (info.path.contains("..") || info.path.contains("//")) { 6722 if (MORE_DEBUG) { 6723 Slog.w(TAG, "Dropping invalid path " + info.path); 6724 } 6725 okay = false; 6726 } 6727 6728 // If the policy is satisfied, go ahead and set up to pipe the 6729 // data to the agent. 6730 if (DEBUG && okay && mAgent != null) { 6731 Slog.i(TAG, "Reusing existing agent instance"); 6732 } 6733 if (okay && mAgent == null) { 6734 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 6735 6736 try { 6737 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 6738 6739 // If we haven't sent any data to this app yet, we probably 6740 // need to clear it first. Check that. 6741 if (!mClearedPackages.contains(pkg)) { 6742 // apps with their own backup agents are 6743 // responsible for coherently managing a full 6744 // restore. 6745 if (mTargetApp.backupAgentName == null) { 6746 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 6747 clearApplicationDataSynchronous(pkg); 6748 } else { 6749 if (DEBUG) Slog.d(TAG, "backup agent (" 6750 + mTargetApp.backupAgentName + ") => no clear"); 6751 } 6752 mClearedPackages.add(pkg); 6753 } else { 6754 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 6755 } 6756 6757 // All set; now set up the IPC and launch the agent 6758 setUpPipes(); 6759 mAgent = bindToAgentSynchronous(mTargetApp, 6760 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 6761 mAgentPackage = pkg; 6762 } catch (IOException e) { 6763 // fall through to error handling 6764 } catch (NameNotFoundException e) { 6765 // fall through to error handling 6766 } 6767 6768 if (mAgent == null) { 6769 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 6770 okay = false; 6771 tearDownPipes(); 6772 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6773 } 6774 } 6775 6776 // Sanity check: make sure we never give data to the wrong app. This 6777 // should never happen but a little paranoia here won't go amiss. 6778 if (okay && !pkg.equals(mAgentPackage)) { 6779 Slog.e(TAG, "Restoring data for " + pkg 6780 + " but agent is for " + mAgentPackage); 6781 okay = false; 6782 } 6783 6784 // At this point we have an agent ready to handle the full 6785 // restore data as well as a pipe for sending data to 6786 // that agent. Tell the agent to start reading from the 6787 // pipe. 6788 if (okay) { 6789 boolean agentSuccess = true; 6790 long toCopy = info.size; 6791 final int token = generateToken(); 6792 try { 6793 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 6794 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 6795 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 6796 + " : " + info.path); 6797 mObbConnection.restoreObbFile(pkg, mPipes[0], 6798 info.size, info.type, info.path, info.mode, 6799 info.mtime, token, mBackupManagerBinder); 6800 } else { 6801 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 6802 + info.path); 6803 // fire up the app's agent listening on the socket. If 6804 // the agent is running in the system process we can't 6805 // just invoke it asynchronously, so we provide a thread 6806 // for it here. 6807 if (mTargetApp.processName.equals("system")) { 6808 Slog.d(TAG, "system process agent - spinning a thread"); 6809 RestoreFileRunnable runner = new RestoreFileRunnable( 6810 mAgent, info, mPipes[0], token); 6811 new Thread(runner, "restore-sys-runner").start(); 6812 } else { 6813 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 6814 info.domain, info.path, info.mode, info.mtime, 6815 token, mBackupManagerBinder); 6816 } 6817 } 6818 } catch (IOException e) { 6819 // couldn't dup the socket for a process-local restore 6820 Slog.d(TAG, "Couldn't establish restore"); 6821 agentSuccess = false; 6822 okay = false; 6823 } catch (RemoteException e) { 6824 // whoops, remote entity went away. We'll eat the content 6825 // ourselves, then, and not copy it over. 6826 Slog.e(TAG, "Agent crashed during full restore"); 6827 agentSuccess = false; 6828 okay = false; 6829 } 6830 6831 // Copy over the data if the agent is still good 6832 if (okay) { 6833 boolean pipeOkay = true; 6834 FileOutputStream pipe = new FileOutputStream( 6835 mPipes[1].getFileDescriptor()); 6836 while (toCopy > 0) { 6837 int toRead = (toCopy > buffer.length) 6838 ? buffer.length : (int)toCopy; 6839 int nRead = instream.read(buffer, 0, toRead); 6840 if (nRead >= 0) mBytes += nRead; 6841 if (nRead <= 0) break; 6842 toCopy -= nRead; 6843 6844 // send it to the output pipe as long as things 6845 // are still good 6846 if (pipeOkay) { 6847 try { 6848 pipe.write(buffer, 0, nRead); 6849 } catch (IOException e) { 6850 Slog.e(TAG, "Failed to write to restore pipe", e); 6851 pipeOkay = false; 6852 } 6853 } 6854 } 6855 6856 // done sending that file! Now we just need to consume 6857 // the delta from info.size to the end of block. 6858 skipTarPadding(info.size, instream); 6859 6860 // and now that we've sent it all, wait for the remote 6861 // side to acknowledge receipt 6862 agentSuccess = waitUntilOperationComplete(token); 6863 } 6864 6865 // okay, if the remote end failed at any point, deal with 6866 // it by ignoring the rest of the restore on it 6867 if (!agentSuccess) { 6868 mBackupHandler.removeMessages(MSG_TIMEOUT); 6869 tearDownPipes(); 6870 tearDownAgent(mTargetApp); 6871 mAgent = null; 6872 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6873 } 6874 } 6875 6876 // Problems setting up the agent communication, or an already- 6877 // ignored package: skip to the next tar stream entry by 6878 // reading and discarding this file. 6879 if (!okay) { 6880 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 6881 long bytesToConsume = (info.size + 511) & ~511; 6882 while (bytesToConsume > 0) { 6883 int toRead = (bytesToConsume > buffer.length) 6884 ? buffer.length : (int)bytesToConsume; 6885 long nRead = instream.read(buffer, 0, toRead); 6886 if (nRead >= 0) mBytes += nRead; 6887 if (nRead <= 0) break; 6888 bytesToConsume -= nRead; 6889 } 6890 } 6891 } 6892 } 6893 } catch (IOException e) { 6894 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 6895 // treat as EOF 6896 info = null; 6897 } 6898 6899 return (info != null); 6900 } 6901 6902 void setUpPipes() throws IOException { 6903 mPipes = ParcelFileDescriptor.createPipe(); 6904 } 6905 6906 void tearDownPipes() { 6907 if (mPipes != null) { 6908 try { 6909 mPipes[0].close(); 6910 mPipes[0] = null; 6911 mPipes[1].close(); 6912 mPipes[1] = null; 6913 } catch (IOException e) { 6914 Slog.w(TAG, "Couldn't close agent pipes", e); 6915 } 6916 mPipes = null; 6917 } 6918 } 6919 6920 void tearDownAgent(ApplicationInfo app) { 6921 if (mAgent != null) { 6922 try { 6923 // unbind and tidy up even on timeout or failure, just in case 6924 mActivityManager.unbindBackupAgent(app); 6925 6926 // The agent was running with a stub Application object, so shut it down. 6927 // !!! We hardcode the confirmation UI's package name here rather than use a 6928 // manifest flag! TODO something less direct. 6929 if (app.uid >= Process.FIRST_APPLICATION_UID 6930 && !app.packageName.equals("com.android.backupconfirm")) { 6931 if (DEBUG) Slog.d(TAG, "Killing host process"); 6932 mActivityManager.killApplicationProcess(app.processName, app.uid); 6933 } else { 6934 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 6935 } 6936 } catch (RemoteException e) { 6937 Slog.d(TAG, "Lost app trying to shut down"); 6938 } 6939 mAgent = null; 6940 } 6941 } 6942 6943 class RestoreInstallObserver extends PackageInstallObserver { 6944 final AtomicBoolean mDone = new AtomicBoolean(); 6945 String mPackageName; 6946 int mResult; 6947 6948 public void reset() { 6949 synchronized (mDone) { 6950 mDone.set(false); 6951 } 6952 } 6953 6954 public void waitForCompletion() { 6955 synchronized (mDone) { 6956 while (mDone.get() == false) { 6957 try { 6958 mDone.wait(); 6959 } catch (InterruptedException e) { } 6960 } 6961 } 6962 } 6963 6964 int getResult() { 6965 return mResult; 6966 } 6967 6968 @Override 6969 public void onPackageInstalled(String packageName, int returnCode, 6970 String msg, Bundle extras) { 6971 synchronized (mDone) { 6972 mResult = returnCode; 6973 mPackageName = packageName; 6974 mDone.set(true); 6975 mDone.notifyAll(); 6976 } 6977 } 6978 } 6979 6980 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 6981 final AtomicBoolean mDone = new AtomicBoolean(); 6982 int mResult; 6983 6984 public void reset() { 6985 synchronized (mDone) { 6986 mDone.set(false); 6987 } 6988 } 6989 6990 public void waitForCompletion() { 6991 synchronized (mDone) { 6992 while (mDone.get() == false) { 6993 try { 6994 mDone.wait(); 6995 } catch (InterruptedException e) { } 6996 } 6997 } 6998 } 6999 7000 @Override 7001 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 7002 synchronized (mDone) { 7003 mResult = returnCode; 7004 mDone.set(true); 7005 mDone.notifyAll(); 7006 } 7007 } 7008 } 7009 7010 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 7011 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 7012 7013 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 7014 boolean okay = true; 7015 7016 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 7017 7018 // The file content is an .apk file. Copy it out to a staging location and 7019 // attempt to install it. 7020 File apkFile = new File(mDataDir, info.packageName); 7021 try { 7022 FileOutputStream apkStream = new FileOutputStream(apkFile); 7023 byte[] buffer = new byte[32 * 1024]; 7024 long size = info.size; 7025 while (size > 0) { 7026 long toRead = (buffer.length < size) ? buffer.length : size; 7027 int didRead = instream.read(buffer, 0, (int)toRead); 7028 if (didRead >= 0) mBytes += didRead; 7029 apkStream.write(buffer, 0, didRead); 7030 size -= didRead; 7031 } 7032 apkStream.close(); 7033 7034 // make sure the installer can read it 7035 apkFile.setReadable(true, false); 7036 7037 // Now install it 7038 Uri packageUri = Uri.fromFile(apkFile); 7039 mInstallObserver.reset(); 7040 mPackageManager.installPackage(packageUri, mInstallObserver, 7041 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 7042 installerPackage); 7043 mInstallObserver.waitForCompletion(); 7044 7045 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 7046 // The only time we continue to accept install of data even if the 7047 // apk install failed is if we had already determined that we could 7048 // accept the data regardless. 7049 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 7050 okay = false; 7051 } 7052 } else { 7053 // Okay, the install succeeded. Make sure it was the right app. 7054 boolean uninstall = false; 7055 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 7056 Slog.w(TAG, "Restore stream claimed to include apk for " 7057 + info.packageName + " but apk was really " 7058 + mInstallObserver.mPackageName); 7059 // delete the package we just put in place; it might be fraudulent 7060 okay = false; 7061 uninstall = true; 7062 } else { 7063 try { 7064 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 7065 PackageManager.GET_SIGNATURES); 7066 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 7067 Slog.w(TAG, "Restore stream contains apk of package " 7068 + info.packageName + " but it disallows backup/restore"); 7069 okay = false; 7070 } else { 7071 // So far so good -- do the signatures match the manifest? 7072 Signature[] sigs = mManifestSignatures.get(info.packageName); 7073 if (signaturesMatch(sigs, pkg)) { 7074 // If this is a system-uid app without a declared backup agent, 7075 // don't restore any of the file data. 7076 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 7077 && (pkg.applicationInfo.backupAgentName == null)) { 7078 Slog.w(TAG, "Installed app " + info.packageName 7079 + " has restricted uid and no agent"); 7080 okay = false; 7081 } 7082 } else { 7083 Slog.w(TAG, "Installed app " + info.packageName 7084 + " signatures do not match restore manifest"); 7085 okay = false; 7086 uninstall = true; 7087 } 7088 } 7089 } catch (NameNotFoundException e) { 7090 Slog.w(TAG, "Install of package " + info.packageName 7091 + " succeeded but now not found"); 7092 okay = false; 7093 } 7094 } 7095 7096 // If we're not okay at this point, we need to delete the package 7097 // that we just installed. 7098 if (uninstall) { 7099 mDeleteObserver.reset(); 7100 mPackageManager.deletePackage(mInstallObserver.mPackageName, 7101 mDeleteObserver, 0); 7102 mDeleteObserver.waitForCompletion(); 7103 } 7104 } 7105 } catch (IOException e) { 7106 Slog.e(TAG, "Unable to transcribe restored apk for install"); 7107 okay = false; 7108 } finally { 7109 apkFile.delete(); 7110 } 7111 7112 return okay; 7113 } 7114 7115 // Given an actual file content size, consume the post-content padding mandated 7116 // by the tar format. 7117 void skipTarPadding(long size, InputStream instream) throws IOException { 7118 long partial = (size + 512) % 512; 7119 if (partial > 0) { 7120 final int needed = 512 - (int)partial; 7121 byte[] buffer = new byte[needed]; 7122 if (readExactly(instream, buffer, 0, needed) == needed) { 7123 mBytes += needed; 7124 } else throw new IOException("Unexpected EOF in padding"); 7125 } 7126 } 7127 7128 // Read a widget metadata file, returning the restored blob 7129 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 7130 // Fail on suspiciously large widget dump files 7131 if (info.size > 64 * 1024) { 7132 throw new IOException("Metadata too big; corrupt? size=" + info.size); 7133 } 7134 7135 byte[] buffer = new byte[(int) info.size]; 7136 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 7137 mBytes += info.size; 7138 } else throw new IOException("Unexpected EOF in widget data"); 7139 7140 String[] str = new String[1]; 7141 int offset = extractLine(buffer, 0, str); 7142 int version = Integer.parseInt(str[0]); 7143 if (version == BACKUP_MANIFEST_VERSION) { 7144 offset = extractLine(buffer, offset, str); 7145 final String pkg = str[0]; 7146 if (info.packageName.equals(pkg)) { 7147 // Data checks out -- the rest of the buffer is a concatenation of 7148 // binary blobs as described in the comment at writeAppWidgetData() 7149 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 7150 offset, buffer.length - offset); 7151 DataInputStream in = new DataInputStream(bin); 7152 while (bin.available() > 0) { 7153 int token = in.readInt(); 7154 int size = in.readInt(); 7155 if (size > 64 * 1024) { 7156 throw new IOException("Datum " 7157 + Integer.toHexString(token) 7158 + " too big; corrupt? size=" + info.size); 7159 } 7160 switch (token) { 7161 case BACKUP_WIDGET_METADATA_TOKEN: 7162 { 7163 if (MORE_DEBUG) { 7164 Slog.i(TAG, "Got widget metadata for " + info.packageName); 7165 } 7166 mWidgetData = new byte[size]; 7167 in.read(mWidgetData); 7168 break; 7169 } 7170 default: 7171 { 7172 if (DEBUG) { 7173 Slog.i(TAG, "Ignoring metadata blob " 7174 + Integer.toHexString(token) 7175 + " for " + info.packageName); 7176 } 7177 in.skipBytes(size); 7178 break; 7179 } 7180 } 7181 } 7182 } else { 7183 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 7184 + " but widget data for " + pkg); 7185 } 7186 } else { 7187 Slog.w(TAG, "Unsupported metadata version " + version); 7188 } 7189 } 7190 7191 // Returns a policy constant; takes a buffer arg to reduce memory churn 7192 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 7193 throws IOException { 7194 // Fail on suspiciously large manifest files 7195 if (info.size > 64 * 1024) { 7196 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 7197 } 7198 7199 byte[] buffer = new byte[(int) info.size]; 7200 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 7201 mBytes += info.size; 7202 } else throw new IOException("Unexpected EOF in manifest"); 7203 7204 RestorePolicy policy = RestorePolicy.IGNORE; 7205 String[] str = new String[1]; 7206 int offset = 0; 7207 7208 try { 7209 offset = extractLine(buffer, offset, str); 7210 int version = Integer.parseInt(str[0]); 7211 if (version == BACKUP_MANIFEST_VERSION) { 7212 offset = extractLine(buffer, offset, str); 7213 String manifestPackage = str[0]; 7214 // TODO: handle <original-package> 7215 if (manifestPackage.equals(info.packageName)) { 7216 offset = extractLine(buffer, offset, str); 7217 version = Integer.parseInt(str[0]); // app version 7218 offset = extractLine(buffer, offset, str); 7219 // This is the platform version, which we don't use, but we parse it 7220 // as a safety against corruption in the manifest. 7221 Integer.parseInt(str[0]); 7222 offset = extractLine(buffer, offset, str); 7223 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 7224 offset = extractLine(buffer, offset, str); 7225 boolean hasApk = str[0].equals("1"); 7226 offset = extractLine(buffer, offset, str); 7227 int numSigs = Integer.parseInt(str[0]); 7228 if (numSigs > 0) { 7229 Signature[] sigs = new Signature[numSigs]; 7230 for (int i = 0; i < numSigs; i++) { 7231 offset = extractLine(buffer, offset, str); 7232 sigs[i] = new Signature(str[0]); 7233 } 7234 mManifestSignatures.put(info.packageName, sigs); 7235 7236 // Okay, got the manifest info we need... 7237 try { 7238 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 7239 info.packageName, PackageManager.GET_SIGNATURES); 7240 // Fall through to IGNORE if the app explicitly disallows backup 7241 final int flags = pkgInfo.applicationInfo.flags; 7242 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 7243 // Restore system-uid-space packages only if they have 7244 // defined a custom backup agent 7245 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 7246 || (pkgInfo.applicationInfo.backupAgentName != null)) { 7247 // Verify signatures against any installed version; if they 7248 // don't match, then we fall though and ignore the data. The 7249 // signatureMatch() method explicitly ignores the signature 7250 // check for packages installed on the system partition, because 7251 // such packages are signed with the platform cert instead of 7252 // the app developer's cert, so they're different on every 7253 // device. 7254 if (signaturesMatch(sigs, pkgInfo)) { 7255 if (pkgInfo.versionCode >= version) { 7256 Slog.i(TAG, "Sig + version match; taking data"); 7257 policy = RestorePolicy.ACCEPT; 7258 } else { 7259 // The data is from a newer version of the app than 7260 // is presently installed. That means we can only 7261 // use it if the matching apk is also supplied. 7262 Slog.d(TAG, "Data version " + version 7263 + " is newer than installed version " 7264 + pkgInfo.versionCode + " - requiring apk"); 7265 policy = RestorePolicy.ACCEPT_IF_APK; 7266 } 7267 } else { 7268 Slog.w(TAG, "Restore manifest signatures do not match " 7269 + "installed application for " + info.packageName); 7270 } 7271 } else { 7272 Slog.w(TAG, "Package " + info.packageName 7273 + " is system level with no agent"); 7274 } 7275 } else { 7276 if (DEBUG) Slog.i(TAG, "Restore manifest from " 7277 + info.packageName + " but allowBackup=false"); 7278 } 7279 } catch (NameNotFoundException e) { 7280 // Okay, the target app isn't installed. We can process 7281 // the restore properly only if the dataset provides the 7282 // apk file and we can successfully install it. 7283 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 7284 + " not installed; requiring apk in dataset"); 7285 policy = RestorePolicy.ACCEPT_IF_APK; 7286 } 7287 7288 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 7289 Slog.i(TAG, "Cannot restore package " + info.packageName 7290 + " without the matching .apk"); 7291 } 7292 } else { 7293 Slog.i(TAG, "Missing signature on backed-up package " 7294 + info.packageName); 7295 } 7296 } else { 7297 Slog.i(TAG, "Expected package " + info.packageName 7298 + " but restore manifest claims " + manifestPackage); 7299 } 7300 } else { 7301 Slog.i(TAG, "Unknown restore manifest version " + version 7302 + " for package " + info.packageName); 7303 } 7304 } catch (NumberFormatException e) { 7305 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 7306 } catch (IllegalArgumentException e) { 7307 Slog.w(TAG, e.getMessage()); 7308 } 7309 7310 return policy; 7311 } 7312 7313 // Builds a line from a byte buffer starting at 'offset', and returns 7314 // the index of the next unconsumed data in the buffer. 7315 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 7316 final int end = buffer.length; 7317 if (offset >= end) throw new IOException("Incomplete data"); 7318 7319 int pos; 7320 for (pos = offset; pos < end; pos++) { 7321 byte c = buffer[pos]; 7322 // at LF we declare end of line, and return the next char as the 7323 // starting point for the next time through 7324 if (c == '\n') { 7325 break; 7326 } 7327 } 7328 outStr[0] = new String(buffer, offset, pos - offset); 7329 pos++; // may be pointing an extra byte past the end but that's okay 7330 return pos; 7331 } 7332 7333 void dumpFileMetadata(FileMetadata info) { 7334 if (DEBUG) { 7335 StringBuilder b = new StringBuilder(128); 7336 7337 // mode string 7338 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 7339 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 7340 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 7341 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 7342 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 7343 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 7344 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 7345 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 7346 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 7347 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 7348 b.append(String.format(" %9d ", info.size)); 7349 7350 Date stamp = new Date(info.mtime); 7351 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 7352 7353 b.append(info.packageName); 7354 b.append(" :: "); 7355 b.append(info.domain); 7356 b.append(" :: "); 7357 b.append(info.path); 7358 7359 Slog.i(TAG, b.toString()); 7360 } 7361 } 7362 // Consume a tar file header block [sequence] and accumulate the relevant metadata 7363 FileMetadata readTarHeaders(InputStream instream) throws IOException { 7364 byte[] block = new byte[512]; 7365 FileMetadata info = null; 7366 7367 boolean gotHeader = readTarHeader(instream, block); 7368 if (gotHeader) { 7369 try { 7370 // okay, presume we're okay, and extract the various metadata 7371 info = new FileMetadata(); 7372 info.size = extractRadix(block, 124, 12, 8); 7373 info.mtime = extractRadix(block, 136, 12, 8); 7374 info.mode = extractRadix(block, 100, 8, 8); 7375 7376 info.path = extractString(block, 345, 155); // prefix 7377 String path = extractString(block, 0, 100); 7378 if (path.length() > 0) { 7379 if (info.path.length() > 0) info.path += '/'; 7380 info.path += path; 7381 } 7382 7383 // tar link indicator field: 1 byte at offset 156 in the header. 7384 int typeChar = block[156]; 7385 if (typeChar == 'x') { 7386 // pax extended header, so we need to read that 7387 gotHeader = readPaxExtendedHeader(instream, info); 7388 if (gotHeader) { 7389 // and after a pax extended header comes another real header -- read 7390 // that to find the real file type 7391 gotHeader = readTarHeader(instream, block); 7392 } 7393 if (!gotHeader) throw new IOException("Bad or missing pax header"); 7394 7395 typeChar = block[156]; 7396 } 7397 7398 switch (typeChar) { 7399 case '0': info.type = BackupAgent.TYPE_FILE; break; 7400 case '5': { 7401 info.type = BackupAgent.TYPE_DIRECTORY; 7402 if (info.size != 0) { 7403 Slog.w(TAG, "Directory entry with nonzero size in header"); 7404 info.size = 0; 7405 } 7406 break; 7407 } 7408 case 0: { 7409 // presume EOF 7410 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 7411 return null; 7412 } 7413 default: { 7414 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 7415 throw new IOException("Unknown entity type " + typeChar); 7416 } 7417 } 7418 7419 // Parse out the path 7420 // 7421 // first: apps/shared/unrecognized 7422 if (FullBackup.SHARED_PREFIX.regionMatches(0, 7423 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 7424 // File in shared storage. !!! TODO: implement this. 7425 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 7426 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 7427 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 7428 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 7429 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 7430 info.path, 0, FullBackup.APPS_PREFIX.length())) { 7431 // App content! Parse out the package name and domain 7432 7433 // strip the apps/ prefix 7434 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 7435 7436 // extract the package name 7437 int slash = info.path.indexOf('/'); 7438 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 7439 info.packageName = info.path.substring(0, slash); 7440 info.path = info.path.substring(slash+1); 7441 7442 // if it's a manifest or metadata payload we're done, otherwise parse 7443 // out the domain into which the file will be restored 7444 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 7445 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 7446 slash = info.path.indexOf('/'); 7447 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path); 7448 info.domain = info.path.substring(0, slash); 7449 info.path = info.path.substring(slash + 1); 7450 } 7451 } 7452 } catch (IOException e) { 7453 if (DEBUG) { 7454 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 7455 HEXLOG(block); 7456 } 7457 throw e; 7458 } 7459 } 7460 return info; 7461 } 7462 7463 private void HEXLOG(byte[] block) { 7464 int offset = 0; 7465 int todo = block.length; 7466 StringBuilder buf = new StringBuilder(64); 7467 while (todo > 0) { 7468 buf.append(String.format("%04x ", offset)); 7469 int numThisLine = (todo > 16) ? 16 : todo; 7470 for (int i = 0; i < numThisLine; i++) { 7471 buf.append(String.format("%02x ", block[offset+i])); 7472 } 7473 Slog.i("hexdump", buf.toString()); 7474 buf.setLength(0); 7475 todo -= numThisLine; 7476 offset += numThisLine; 7477 } 7478 } 7479 7480 // Read exactly the given number of bytes into a buffer at the stated offset. 7481 // Returns false if EOF is encountered before the requested number of bytes 7482 // could be read. 7483 int readExactly(InputStream in, byte[] buffer, int offset, int size) 7484 throws IOException { 7485 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 7486 7487 int soFar = 0; 7488 while (soFar < size) { 7489 int nRead = in.read(buffer, offset + soFar, size - soFar); 7490 if (nRead <= 0) { 7491 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 7492 break; 7493 } 7494 soFar += nRead; 7495 } 7496 return soFar; 7497 } 7498 7499 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 7500 final int got = readExactly(instream, block, 0, 512); 7501 if (got == 0) return false; // Clean EOF 7502 if (got < 512) throw new IOException("Unable to read full block header"); 7503 mBytes += 512; 7504 return true; 7505 } 7506 7507 // overwrites 'info' fields based on the pax extended header 7508 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 7509 throws IOException { 7510 // We should never see a pax extended header larger than this 7511 if (info.size > 32*1024) { 7512 Slog.w(TAG, "Suspiciously large pax header size " + info.size 7513 + " - aborting"); 7514 throw new IOException("Sanity failure: pax header size " + info.size); 7515 } 7516 7517 // read whole blocks, not just the content size 7518 int numBlocks = (int)((info.size + 511) >> 9); 7519 byte[] data = new byte[numBlocks * 512]; 7520 if (readExactly(instream, data, 0, data.length) < data.length) { 7521 throw new IOException("Unable to read full pax header"); 7522 } 7523 mBytes += data.length; 7524 7525 final int contentSize = (int) info.size; 7526 int offset = 0; 7527 do { 7528 // extract the line at 'offset' 7529 int eol = offset+1; 7530 while (eol < contentSize && data[eol] != ' ') eol++; 7531 if (eol >= contentSize) { 7532 // error: we just hit EOD looking for the end of the size field 7533 throw new IOException("Invalid pax data"); 7534 } 7535 // eol points to the space between the count and the key 7536 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 7537 int key = eol + 1; // start of key=value 7538 eol = offset + linelen - 1; // trailing LF 7539 int value; 7540 for (value = key+1; data[value] != '=' && value <= eol; value++); 7541 if (value > eol) { 7542 throw new IOException("Invalid pax declaration"); 7543 } 7544 7545 // pax requires that key/value strings be in UTF-8 7546 String keyStr = new String(data, key, value-key, "UTF-8"); 7547 // -1 to strip the trailing LF 7548 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 7549 7550 if ("path".equals(keyStr)) { 7551 info.path = valStr; 7552 } else if ("size".equals(keyStr)) { 7553 info.size = Long.parseLong(valStr); 7554 } else { 7555 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 7556 } 7557 7558 offset += linelen; 7559 } while (offset < contentSize); 7560 7561 return true; 7562 } 7563 7564 long extractRadix(byte[] data, int offset, int maxChars, int radix) 7565 throws IOException { 7566 long value = 0; 7567 final int end = offset + maxChars; 7568 for (int i = offset; i < end; i++) { 7569 final byte b = data[i]; 7570 // Numeric fields in tar can terminate with either NUL or SPC 7571 if (b == 0 || b == ' ') break; 7572 if (b < '0' || b > ('0' + radix - 1)) { 7573 throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix); 7574 } 7575 value = radix * value + (b - '0'); 7576 } 7577 return value; 7578 } 7579 7580 String extractString(byte[] data, int offset, int maxChars) throws IOException { 7581 final int end = offset + maxChars; 7582 int eos = offset; 7583 // tar string fields terminate early with a NUL 7584 while (eos < end && data[eos] != 0) eos++; 7585 return new String(data, offset, eos-offset, "US-ASCII"); 7586 } 7587 7588 void sendStartRestore() { 7589 if (mObserver != null) { 7590 try { 7591 mObserver.onStartRestore(); 7592 } catch (RemoteException e) { 7593 Slog.w(TAG, "full restore observer went away: startRestore"); 7594 mObserver = null; 7595 } 7596 } 7597 } 7598 7599 void sendOnRestorePackage(String name) { 7600 if (mObserver != null) { 7601 try { 7602 // TODO: use a more user-friendly name string 7603 mObserver.onRestorePackage(name); 7604 } catch (RemoteException e) { 7605 Slog.w(TAG, "full restore observer went away: restorePackage"); 7606 mObserver = null; 7607 } 7608 } 7609 } 7610 7611 void sendEndRestore() { 7612 if (mObserver != null) { 7613 try { 7614 mObserver.onEndRestore(); 7615 } catch (RemoteException e) { 7616 Slog.w(TAG, "full restore observer went away: endRestore"); 7617 mObserver = null; 7618 } 7619 } 7620 } 7621 } 7622 7623 // ----- Restore handling ----- 7624 7625 // Old style: directly match the stored vs on device signature blocks 7626 static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 7627 if (target == null) { 7628 return false; 7629 } 7630 7631 // If the target resides on the system partition, we allow it to restore 7632 // data from the like-named package in a restore set even if the signatures 7633 // do not match. (Unlike general applications, those flashed to the system 7634 // partition will be signed with the device's platform certificate, so on 7635 // different phones the same system app will have different signatures.) 7636 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 7637 if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 7638 return true; 7639 } 7640 7641 // Allow unsigned apps, but not signed on one device and unsigned on the other 7642 // !!! TODO: is this the right policy? 7643 Signature[] deviceSigs = target.signatures; 7644 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs 7645 + " device=" + deviceSigs); 7646 if ((storedSigs == null || storedSigs.length == 0) 7647 && (deviceSigs == null || deviceSigs.length == 0)) { 7648 return true; 7649 } 7650 if (storedSigs == null || deviceSigs == null) { 7651 return false; 7652 } 7653 7654 // !!! TODO: this demands that every stored signature match one 7655 // that is present on device, and does not demand the converse. 7656 // Is this this right policy? 7657 int nStored = storedSigs.length; 7658 int nDevice = deviceSigs.length; 7659 7660 for (int i=0; i < nStored; i++) { 7661 boolean match = false; 7662 for (int j=0; j < nDevice; j++) { 7663 if (storedSigs[i].equals(deviceSigs[j])) { 7664 match = true; 7665 break; 7666 } 7667 } 7668 if (!match) { 7669 return false; 7670 } 7671 } 7672 return true; 7673 } 7674 7675 // Used by both incremental and full restore 7676 void restoreWidgetData(String packageName, byte[] widgetData) { 7677 // Apply the restored widget state and generate the ID update for the app 7678 // TODO: http://b/22388012 7679 AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM); 7680 } 7681 7682 // ***************************** 7683 // NEW UNIFIED RESTORE IMPLEMENTATION 7684 // ***************************** 7685 7686 // states of the unified-restore state machine 7687 enum UnifiedRestoreState { 7688 INITIAL, 7689 RUNNING_QUEUE, 7690 RESTORE_KEYVALUE, 7691 RESTORE_FULL, 7692 RESTORE_FINISHED, 7693 FINAL 7694 } 7695 7696 class PerformUnifiedRestoreTask implements BackupRestoreTask { 7697 // Transport we're working with to do the restore 7698 private IBackupTransport mTransport; 7699 7700 // Where per-transport saved state goes 7701 File mStateDir; 7702 7703 // Restore observer; may be null 7704 private IRestoreObserver mObserver; 7705 7706 // Token identifying the dataset to the transport 7707 private long mToken; 7708 7709 // When this is a restore-during-install, this is the token identifying the 7710 // operation to the Package Manager, and we must ensure that we let it know 7711 // when we're finished. 7712 private int mPmToken; 7713 7714 // When this is restore-during-install, we need to tell the package manager 7715 // whether we actually launched the app, because this affects notifications 7716 // around externally-visible state transitions. 7717 private boolean mDidLaunch; 7718 7719 // Is this a whole-system restore, i.e. are we establishing a new ancestral 7720 // dataset to base future restore-at-install operations from? 7721 private boolean mIsSystemRestore; 7722 7723 // If this is a single-package restore, what package are we interested in? 7724 private PackageInfo mTargetPackage; 7725 7726 // In all cases, the calculated list of packages that we are trying to restore 7727 private List<PackageInfo> mAcceptSet; 7728 7729 // Our bookkeeping about the ancestral dataset 7730 private PackageManagerBackupAgent mPmAgent; 7731 7732 // Currently-bound backup agent for restore + restoreFinished purposes 7733 private IBackupAgent mAgent; 7734 7735 // What sort of restore we're doing now 7736 private RestoreDescription mRestoreDescription; 7737 7738 // The package we're currently restoring 7739 private PackageInfo mCurrentPackage; 7740 7741 // Widget-related data handled as part of this restore operation 7742 private byte[] mWidgetData; 7743 7744 // Number of apps restored in this pass 7745 private int mCount; 7746 7747 // When did we start? 7748 private long mStartRealtime; 7749 7750 // State machine progress 7751 private UnifiedRestoreState mState; 7752 7753 // How are things going? 7754 private int mStatus; 7755 7756 // Done? 7757 private boolean mFinished; 7758 7759 // Key/value: bookkeeping about staged data and files for agent access 7760 private File mBackupDataName; 7761 private File mStageName; 7762 private File mSavedStateName; 7763 private File mNewStateName; 7764 ParcelFileDescriptor mBackupData; 7765 ParcelFileDescriptor mNewState; 7766 7767 // Invariant: mWakelock is already held, and this task is responsible for 7768 // releasing it at the end of the restore operation. 7769 PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer, 7770 long restoreSetToken, PackageInfo targetPackage, int pmToken, 7771 boolean isFullSystemRestore, String[] filterSet) { 7772 mState = UnifiedRestoreState.INITIAL; 7773 mStartRealtime = SystemClock.elapsedRealtime(); 7774 7775 mTransport = transport; 7776 mObserver = observer; 7777 mToken = restoreSetToken; 7778 mPmToken = pmToken; 7779 mTargetPackage = targetPackage; 7780 mIsSystemRestore = isFullSystemRestore; 7781 mFinished = false; 7782 mDidLaunch = false; 7783 7784 if (targetPackage != null) { 7785 // Single package restore 7786 mAcceptSet = new ArrayList<PackageInfo>(); 7787 mAcceptSet.add(targetPackage); 7788 } else { 7789 // Everything possible, or a target set 7790 if (filterSet == null) { 7791 // We want everything and a pony 7792 List<PackageInfo> apps = 7793 PackageManagerBackupAgent.getStorableApplications(mPackageManager); 7794 filterSet = packagesToNames(apps); 7795 if (DEBUG) { 7796 Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps"); 7797 } 7798 } 7799 7800 mAcceptSet = new ArrayList<PackageInfo>(filterSet.length); 7801 7802 // Pro tem, we insist on moving the settings provider package to last place. 7803 // Keep track of whether it's in the list, and bump it down if so. We also 7804 // want to do the system package itself first if it's called for. 7805 boolean hasSystem = false; 7806 boolean hasSettings = false; 7807 for (int i = 0; i < filterSet.length; i++) { 7808 try { 7809 PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0); 7810 if ("android".equals(info.packageName)) { 7811 hasSystem = true; 7812 continue; 7813 } 7814 if (SETTINGS_PACKAGE.equals(info.packageName)) { 7815 hasSettings = true; 7816 continue; 7817 } 7818 7819 if (appIsEligibleForBackup(info.applicationInfo)) { 7820 mAcceptSet.add(info); 7821 } 7822 } catch (NameNotFoundException e) { 7823 // requested package name doesn't exist; ignore it 7824 } 7825 } 7826 if (hasSystem) { 7827 try { 7828 mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0)); 7829 } catch (NameNotFoundException e) { 7830 // won't happen; we know a priori that it's valid 7831 } 7832 } 7833 if (hasSettings) { 7834 try { 7835 mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0)); 7836 } catch (NameNotFoundException e) { 7837 // this one is always valid too 7838 } 7839 } 7840 } 7841 7842 if (MORE_DEBUG) { 7843 Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size()); 7844 for (PackageInfo info : mAcceptSet) { 7845 Slog.v(TAG, " " + info.packageName); 7846 } 7847 } 7848 } 7849 7850 private String[] packagesToNames(List<PackageInfo> apps) { 7851 final int N = apps.size(); 7852 String[] names = new String[N]; 7853 for (int i = 0; i < N; i++) { 7854 names[i] = apps.get(i).packageName; 7855 } 7856 return names; 7857 } 7858 7859 // Execute one tick of whatever state machine the task implements 7860 @Override 7861 public void execute() { 7862 if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState); 7863 switch (mState) { 7864 case INITIAL: 7865 startRestore(); 7866 break; 7867 7868 case RUNNING_QUEUE: 7869 dispatchNextRestore(); 7870 break; 7871 7872 case RESTORE_KEYVALUE: 7873 restoreKeyValue(); 7874 break; 7875 7876 case RESTORE_FULL: 7877 restoreFull(); 7878 break; 7879 7880 case RESTORE_FINISHED: 7881 restoreFinished(); 7882 break; 7883 7884 case FINAL: 7885 if (!mFinished) finalizeRestore(); 7886 else { 7887 Slog.e(TAG, "Duplicate finish"); 7888 } 7889 mFinished = true; 7890 break; 7891 } 7892 } 7893 7894 /* 7895 * SKETCH OF OPERATION 7896 * 7897 * create one of these PerformUnifiedRestoreTask objects, telling it which 7898 * dataset & transport to address, and then parameters within the restore 7899 * operation: single target package vs many, etc. 7900 * 7901 * 1. transport.startRestore(token, list-of-packages). If we need @pm@ it is 7902 * always placed first and the settings provider always placed last [for now]. 7903 * 7904 * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline] 7905 * 7906 * [ state change => RUNNING_QUEUE ] 7907 * 7908 * NOW ITERATE: 7909 * 7910 * { 3. t.nextRestorePackage() 7911 * 4. does the metadata for this package allow us to restore it? 7912 * does the on-disk app permit us to restore it? [re-check allowBackup etc] 7913 * 5. is this a key/value dataset? => key/value agent restore 7914 * [ state change => RESTORE_KEYVALUE ] 7915 * 5a. spin up agent 7916 * 5b. t.getRestoreData() to stage it properly 7917 * 5c. call into agent to perform restore 7918 * 5d. tear down agent 7919 * [ state change => RUNNING_QUEUE ] 7920 * 7921 * 6. else it's a stream dataset: 7922 * [ state change => RESTORE_FULL ] 7923 * 6a. instantiate the engine for a stream restore: engine handles agent lifecycles 7924 * 6b. spin off engine runner on separate thread 7925 * 6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket 7926 * [ state change => RUNNING_QUEUE ] 7927 * } 7928 * 7929 * [ state change => FINAL ] 7930 * 7931 * 7. t.finishRestore(), release wakelock, etc. 7932 * 7933 * 7934 */ 7935 7936 // state INITIAL : set up for the restore and read the metadata if necessary 7937 private void startRestore() { 7938 sendStartRestore(mAcceptSet.size()); 7939 7940 // If we're starting a full-system restore, set up to begin widget ID remapping 7941 if (mIsSystemRestore) { 7942 // TODO: http://b/22388012 7943 AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM); 7944 } 7945 7946 try { 7947 String transportDir = mTransport.transportDirName(); 7948 mStateDir = new File(mBaseStateDir, transportDir); 7949 7950 // Fetch the current metadata from the dataset first 7951 PackageInfo pmPackage = new PackageInfo(); 7952 pmPackage.packageName = PACKAGE_MANAGER_SENTINEL; 7953 mAcceptSet.add(0, pmPackage); 7954 7955 PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]); 7956 mStatus = mTransport.startRestore(mToken, packages); 7957 if (mStatus != BackupTransport.TRANSPORT_OK) { 7958 Slog.e(TAG, "Transport error " + mStatus + "; no restore possible"); 7959 mStatus = BackupTransport.TRANSPORT_ERROR; 7960 executeNextState(UnifiedRestoreState.FINAL); 7961 return; 7962 } 7963 7964 RestoreDescription desc = mTransport.nextRestorePackage(); 7965 if (desc == null) { 7966 Slog.e(TAG, "No restore metadata available; halting"); 7967 mStatus = BackupTransport.TRANSPORT_ERROR; 7968 executeNextState(UnifiedRestoreState.FINAL); 7969 return; 7970 } 7971 if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) { 7972 Slog.e(TAG, "Required metadata but got " + desc.getPackageName()); 7973 mStatus = BackupTransport.TRANSPORT_ERROR; 7974 executeNextState(UnifiedRestoreState.FINAL); 7975 return; 7976 } 7977 7978 // Pull the Package Manager metadata from the restore set first 7979 mCurrentPackage = new PackageInfo(); 7980 mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL; 7981 mPmAgent = new PackageManagerBackupAgent(mPackageManager, null); 7982 mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind()); 7983 if (MORE_DEBUG) { 7984 Slog.v(TAG, "initiating restore for PMBA"); 7985 } 7986 initiateOneRestore(mCurrentPackage, 0); 7987 // The PM agent called operationComplete() already, because our invocation 7988 // of it is process-local and therefore synchronous. That means that the 7989 // next-state message (RUNNING_QUEUE) is already enqueued. Only if we're 7990 // unable to proceed with running the queue do we remove that pending 7991 // message and jump straight to the FINAL state. Because this was 7992 // synchronous we also know that we should cancel the pending timeout 7993 // message. 7994 mBackupHandler.removeMessages(MSG_TIMEOUT); 7995 7996 // Verify that the backup set includes metadata. If not, we can't do 7997 // signature/version verification etc, so we simply do not proceed with 7998 // the restore operation. 7999 if (!mPmAgent.hasMetadata()) { 8000 Slog.e(TAG, "No restore metadata available, so not restoring"); 8001 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8002 PACKAGE_MANAGER_SENTINEL, 8003 "Package manager restore metadata missing"); 8004 mStatus = BackupTransport.TRANSPORT_ERROR; 8005 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 8006 executeNextState(UnifiedRestoreState.FINAL); 8007 return; 8008 } 8009 8010 // Success; cache the metadata and continue as expected with the 8011 // next state already enqueued 8012 8013 } catch (RemoteException e) { 8014 // If we lost the transport at any time, halt 8015 Slog.e(TAG, "Unable to contact transport for restore"); 8016 mStatus = BackupTransport.TRANSPORT_ERROR; 8017 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 8018 executeNextState(UnifiedRestoreState.FINAL); 8019 return; 8020 } 8021 } 8022 8023 // state RUNNING_QUEUE : figure out what the next thing to be restored is, 8024 // and fire the appropriate next step 8025 private void dispatchNextRestore() { 8026 UnifiedRestoreState nextState = UnifiedRestoreState.FINAL; 8027 try { 8028 mRestoreDescription = mTransport.nextRestorePackage(); 8029 final String pkgName = (mRestoreDescription != null) 8030 ? mRestoreDescription.getPackageName() : null; 8031 if (pkgName == null) { 8032 Slog.e(TAG, "Failure getting next package name"); 8033 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8034 nextState = UnifiedRestoreState.FINAL; 8035 return; 8036 } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) { 8037 // Yay we've reached the end cleanly 8038 if (DEBUG) { 8039 Slog.v(TAG, "No more packages; finishing restore"); 8040 } 8041 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); 8042 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis); 8043 nextState = UnifiedRestoreState.FINAL; 8044 return; 8045 } 8046 8047 if (DEBUG) { 8048 Slog.i(TAG, "Next restore package: " + mRestoreDescription); 8049 } 8050 sendOnRestorePackage(pkgName); 8051 8052 Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName); 8053 if (metaInfo == null) { 8054 Slog.e(TAG, "No metadata for " + pkgName); 8055 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 8056 "Package metadata missing"); 8057 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8058 return; 8059 } 8060 8061 try { 8062 mCurrentPackage = mPackageManager.getPackageInfo( 8063 pkgName, PackageManager.GET_SIGNATURES); 8064 } catch (NameNotFoundException e) { 8065 // Whoops, we thought we could restore this package but it 8066 // turns out not to be present. Skip it. 8067 Slog.e(TAG, "Package not present: " + pkgName); 8068 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 8069 "Package missing on device"); 8070 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8071 return; 8072 } 8073 8074 if (metaInfo.versionCode > mCurrentPackage.versionCode) { 8075 // Data is from a "newer" version of the app than we have currently 8076 // installed. If the app has not declared that it is prepared to 8077 // handle this case, we do not attempt the restore. 8078 if ((mCurrentPackage.applicationInfo.flags 8079 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { 8080 String message = "Version " + metaInfo.versionCode 8081 + " > installed version " + mCurrentPackage.versionCode; 8082 Slog.w(TAG, "Package " + pkgName + ": " + message); 8083 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8084 pkgName, message); 8085 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8086 return; 8087 } else { 8088 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode 8089 + " > installed " + mCurrentPackage.versionCode 8090 + " but restoreAnyVersion"); 8091 } 8092 } 8093 8094 if (MORE_DEBUG) Slog.v(TAG, "Package " + pkgName 8095 + " restore version [" + metaInfo.versionCode 8096 + "] is compatible with installed version [" 8097 + mCurrentPackage.versionCode + "]"); 8098 8099 // Reset per-package preconditions and fire the appropriate next state 8100 mWidgetData = null; 8101 final int type = mRestoreDescription.getDataType(); 8102 if (type == RestoreDescription.TYPE_KEY_VALUE) { 8103 nextState = UnifiedRestoreState.RESTORE_KEYVALUE; 8104 } else if (type == RestoreDescription.TYPE_FULL_STREAM) { 8105 nextState = UnifiedRestoreState.RESTORE_FULL; 8106 } else { 8107 // Unknown restore type; ignore this package and move on 8108 Slog.e(TAG, "Unrecognized restore type " + type); 8109 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8110 return; 8111 } 8112 } catch (RemoteException e) { 8113 Slog.e(TAG, "Can't get next target from transport; ending restore"); 8114 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8115 nextState = UnifiedRestoreState.FINAL; 8116 return; 8117 } finally { 8118 executeNextState(nextState); 8119 } 8120 } 8121 8122 // state RESTORE_KEYVALUE : restore one package via key/value API set 8123 private void restoreKeyValue() { 8124 // Initiating the restore will pass responsibility for the state machine's 8125 // progress to the agent callback, so we do not always execute the 8126 // next state here. 8127 final String packageName = mCurrentPackage.packageName; 8128 // Validate some semantic requirements that apply in this way 8129 // only to the key/value restore API flow 8130 if (mCurrentPackage.applicationInfo.backupAgentName == null 8131 || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) { 8132 if (MORE_DEBUG) { 8133 Slog.i(TAG, "Data exists for package " + packageName 8134 + " but app has no agent; skipping"); 8135 } 8136 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8137 "Package has no agent"); 8138 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8139 return; 8140 } 8141 8142 Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); 8143 if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { 8144 Slog.w(TAG, "Signature mismatch restoring " + packageName); 8145 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8146 "Signature mismatch"); 8147 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8148 return; 8149 } 8150 8151 // Good to go! Set up and bind the agent... 8152 mAgent = bindToAgentSynchronous( 8153 mCurrentPackage.applicationInfo, 8154 IApplicationThread.BACKUP_MODE_INCREMENTAL); 8155 if (mAgent == null) { 8156 Slog.w(TAG, "Can't find backup agent for " + packageName); 8157 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8158 "Restore agent missing"); 8159 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8160 return; 8161 } 8162 8163 // Whatever happens next, we've launched the target app now; remember that. 8164 mDidLaunch = true; 8165 8166 // And then finally start the restore on this agent 8167 try { 8168 initiateOneRestore(mCurrentPackage, metaInfo.versionCode); 8169 ++mCount; 8170 } catch (Exception e) { 8171 Slog.e(TAG, "Error when attempting restore: " + e.toString()); 8172 keyValueAgentErrorCleanup(); 8173 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8174 } 8175 } 8176 8177 // Guts of a key/value restore operation 8178 void initiateOneRestore(PackageInfo app, int appVersionCode) { 8179 final String packageName = app.packageName; 8180 8181 if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName); 8182 8183 // !!! TODO: get the dirs from the transport 8184 mBackupDataName = new File(mDataDir, packageName + ".restore"); 8185 mStageName = new File(mDataDir, packageName + ".stage"); 8186 mNewStateName = new File(mStateDir, packageName + ".new"); 8187 mSavedStateName = new File(mStateDir, packageName); 8188 8189 // don't stage the 'android' package where the wallpaper data lives. this is 8190 // an optimization: we know there's no widget data hosted/published by that 8191 // package, and this way we avoid doing a spurious copy of MB-sized wallpaper 8192 // data following the download. 8193 boolean staging = !packageName.equals("android"); 8194 ParcelFileDescriptor stage; 8195 File downloadFile = (staging) ? mStageName : mBackupDataName; 8196 8197 final int token = generateToken(); 8198 try { 8199 // Run the transport's restore pass 8200 stage = ParcelFileDescriptor.open(downloadFile, 8201 ParcelFileDescriptor.MODE_READ_WRITE | 8202 ParcelFileDescriptor.MODE_CREATE | 8203 ParcelFileDescriptor.MODE_TRUNCATE); 8204 8205 if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) { 8206 // Transport-level failure, so we wind everything up and 8207 // terminate the restore operation. 8208 Slog.e(TAG, "Error getting restore data for " + packageName); 8209 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8210 stage.close(); 8211 downloadFile.delete(); 8212 executeNextState(UnifiedRestoreState.FINAL); 8213 return; 8214 } 8215 8216 // We have the data from the transport. Now we extract and strip 8217 // any per-package metadata (typically widget-related information) 8218 // if appropriate 8219 if (staging) { 8220 stage.close(); 8221 stage = ParcelFileDescriptor.open(downloadFile, 8222 ParcelFileDescriptor.MODE_READ_ONLY); 8223 8224 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 8225 ParcelFileDescriptor.MODE_READ_WRITE | 8226 ParcelFileDescriptor.MODE_CREATE | 8227 ParcelFileDescriptor.MODE_TRUNCATE); 8228 8229 BackupDataInput in = new BackupDataInput(stage.getFileDescriptor()); 8230 BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor()); 8231 byte[] buffer = new byte[8192]; // will grow when needed 8232 while (in.readNextHeader()) { 8233 final String key = in.getKey(); 8234 final int size = in.getDataSize(); 8235 8236 // is this a special key? 8237 if (key.equals(KEY_WIDGET_STATE)) { 8238 if (DEBUG) { 8239 Slog.i(TAG, "Restoring widget state for " + packageName); 8240 } 8241 mWidgetData = new byte[size]; 8242 in.readEntityData(mWidgetData, 0, size); 8243 } else { 8244 if (size > buffer.length) { 8245 buffer = new byte[size]; 8246 } 8247 in.readEntityData(buffer, 0, size); 8248 out.writeEntityHeader(key, size); 8249 out.writeEntityData(buffer, size); 8250 } 8251 } 8252 8253 mBackupData.close(); 8254 } 8255 8256 // Okay, we have the data. Now have the agent do the restore. 8257 stage.close(); 8258 8259 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 8260 ParcelFileDescriptor.MODE_READ_ONLY); 8261 8262 mNewState = ParcelFileDescriptor.open(mNewStateName, 8263 ParcelFileDescriptor.MODE_READ_WRITE | 8264 ParcelFileDescriptor.MODE_CREATE | 8265 ParcelFileDescriptor.MODE_TRUNCATE); 8266 8267 // Kick off the restore, checking for hung agents. The timeout or 8268 // the operationComplete() callback will schedule the next step, 8269 // so we do not do that here. 8270 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this); 8271 mAgent.doRestore(mBackupData, appVersionCode, mNewState, 8272 token, mBackupManagerBinder); 8273 } catch (Exception e) { 8274 Slog.e(TAG, "Unable to call app for restore: " + packageName, e); 8275 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8276 packageName, e.toString()); 8277 keyValueAgentErrorCleanup(); // clears any pending timeout messages as well 8278 8279 // After a restore failure we go back to running the queue. If there 8280 // are no more packages to be restored that will be handled by the 8281 // next step. 8282 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8283 } 8284 } 8285 8286 // state RESTORE_FULL : restore one package via streaming engine 8287 private void restoreFull() { 8288 // None of this can run on the work looper here, so we spin asynchronous 8289 // work like this: 8290 // 8291 // StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk() 8292 // write it into the pipe to the engine 8293 // EngineThread: FullRestoreEngine thread communicating with the target app 8294 // 8295 // When finished, StreamFeederThread executes next state as appropriate on the 8296 // backup looper, and the overall unified restore task resumes 8297 try { 8298 StreamFeederThread feeder = new StreamFeederThread(); 8299 if (MORE_DEBUG) { 8300 Slog.i(TAG, "Spinning threads for stream restore of " 8301 + mCurrentPackage.packageName); 8302 } 8303 new Thread(feeder, "unified-stream-feeder").start(); 8304 8305 // At this point the feeder is responsible for advancing the restore 8306 // state, so we're done here. 8307 } catch (IOException e) { 8308 // Unable to instantiate the feeder thread -- we need to bail on the 8309 // current target. We haven't asked the transport for data yet, though, 8310 // so we can do that simply by going back to running the restore queue. 8311 Slog.e(TAG, "Unable to construct pipes for stream restore!"); 8312 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8313 } 8314 } 8315 8316 // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end 8317 private void restoreFinished() { 8318 try { 8319 final int token = generateToken(); 8320 prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this); 8321 mAgent.doRestoreFinished(token, mBackupManagerBinder); 8322 // If we get this far, the callback or timeout will schedule the 8323 // next restore state, so we're done 8324 } catch (Exception e) { 8325 final String packageName = mCurrentPackage.packageName; 8326 Slog.e(TAG, "Unable to finalize restore of " + packageName); 8327 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8328 packageName, e.toString()); 8329 keyValueAgentErrorCleanup(); 8330 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8331 } 8332 } 8333 8334 class StreamFeederThread extends RestoreEngine implements Runnable { 8335 final String TAG = "StreamFeederThread"; 8336 FullRestoreEngine mEngine; 8337 8338 // pipe through which we read data from the transport. [0] read, [1] write 8339 ParcelFileDescriptor[] mTransportPipes; 8340 8341 // pipe through which the engine will read data. [0] read, [1] write 8342 ParcelFileDescriptor[] mEnginePipes; 8343 8344 public StreamFeederThread() throws IOException { 8345 mTransportPipes = ParcelFileDescriptor.createPipe(); 8346 mEnginePipes = ParcelFileDescriptor.createPipe(); 8347 setRunning(true); 8348 } 8349 8350 @Override 8351 public void run() { 8352 UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE; 8353 int status = BackupTransport.TRANSPORT_OK; 8354 8355 EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE, 8356 mCurrentPackage.packageName); 8357 8358 mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false); 8359 EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]); 8360 8361 ParcelFileDescriptor eWriteEnd = mEnginePipes[1]; 8362 ParcelFileDescriptor tReadEnd = mTransportPipes[0]; 8363 ParcelFileDescriptor tWriteEnd = mTransportPipes[1]; 8364 8365 int bufferSize = 32 * 1024; 8366 byte[] buffer = new byte[bufferSize]; 8367 FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor()); 8368 FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor()); 8369 8370 // spin up the engine and start moving data to it 8371 new Thread(eThread, "unified-restore-engine").start(); 8372 8373 try { 8374 while (status == BackupTransport.TRANSPORT_OK) { 8375 // have the transport write some of the restoring data to us 8376 int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd); 8377 if (result > 0) { 8378 // The transport wrote this many bytes of restore data to the 8379 // pipe, so pass it along to the engine. 8380 if (MORE_DEBUG) { 8381 Slog.v(TAG, " <- transport provided chunk size " + result); 8382 } 8383 if (result > bufferSize) { 8384 bufferSize = result; 8385 buffer = new byte[bufferSize]; 8386 } 8387 int toCopy = result; 8388 while (toCopy > 0) { 8389 int n = transportIn.read(buffer, 0, toCopy); 8390 engineOut.write(buffer, 0, n); 8391 toCopy -= n; 8392 if (MORE_DEBUG) { 8393 Slog.v(TAG, " -> wrote " + n + " to engine, left=" + toCopy); 8394 } 8395 } 8396 } else if (result == BackupTransport.NO_MORE_DATA) { 8397 // Clean finish. Wind up and we're done! 8398 if (MORE_DEBUG) { 8399 Slog.i(TAG, "Got clean full-restore EOF for " 8400 + mCurrentPackage.packageName); 8401 } 8402 status = BackupTransport.TRANSPORT_OK; 8403 break; 8404 } else { 8405 // Transport reported some sort of failure; the fall-through 8406 // handling will deal properly with that. 8407 Slog.e(TAG, "Error " + result + " streaming restore for " 8408 + mCurrentPackage.packageName); 8409 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8410 status = result; 8411 } 8412 } 8413 if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through"); 8414 } catch (IOException e) { 8415 // We lost our ability to communicate via the pipes. That's worrying 8416 // but potentially recoverable; abandon this package's restore but 8417 // carry on with the next restore target. 8418 Slog.e(TAG, "Unable to route data for restore"); 8419 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8420 mCurrentPackage.packageName, "I/O error on pipes"); 8421 status = BackupTransport.AGENT_ERROR; 8422 } catch (RemoteException e) { 8423 // The transport went away; terminate the whole operation. Closing 8424 // the sockets will wake up the engine and it will then tidy up the 8425 // remote end. 8426 Slog.e(TAG, "Transport failed during restore"); 8427 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8428 status = BackupTransport.TRANSPORT_ERROR; 8429 } finally { 8430 // Close the transport pipes and *our* end of the engine pipe, 8431 // but leave the engine thread's end open so that it properly 8432 // hits EOF and winds up its operations. 8433 IoUtils.closeQuietly(mEnginePipes[1]); 8434 IoUtils.closeQuietly(mTransportPipes[0]); 8435 IoUtils.closeQuietly(mTransportPipes[1]); 8436 8437 // Don't proceed until the engine has finished 8438 eThread.waitForResult(); 8439 8440 if (MORE_DEBUG) { 8441 Slog.i(TAG, "engine thread finished; proceeding"); 8442 } 8443 8444 // Now we're really done with this one too 8445 IoUtils.closeQuietly(mEnginePipes[0]); 8446 8447 // In all cases we want to remember whether we launched 8448 // the target app as part of our work so far. 8449 mDidLaunch = (mEngine.getAgent() != null); 8450 8451 // If we hit a transport-level error, we are done with everything; 8452 // if we hit an agent error we just go back to running the queue. 8453 if (status == BackupTransport.TRANSPORT_OK) { 8454 // Clean finish means we issue the restore-finished callback 8455 nextState = UnifiedRestoreState.RESTORE_FINISHED; 8456 8457 // the engine bound the target's agent, so recover that binding 8458 // to use for the callback. 8459 mAgent = mEngine.getAgent(); 8460 } else { 8461 // Something went wrong somewhere. Whether it was at the transport 8462 // level is immaterial; we need to tell the transport to bail 8463 try { 8464 mTransport.abortFullRestore(); 8465 } catch (RemoteException e) { 8466 // transport itself is dead; make sure we handle this as a 8467 // fatal error 8468 status = BackupTransport.TRANSPORT_ERROR; 8469 } 8470 8471 // We also need to wipe the current target's data, as it's probably 8472 // in an incoherent state. 8473 clearApplicationDataSynchronous(mCurrentPackage.packageName); 8474 8475 // Schedule the next state based on the nature of our failure 8476 if (status == BackupTransport.TRANSPORT_ERROR) { 8477 nextState = UnifiedRestoreState.FINAL; 8478 } else { 8479 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8480 } 8481 } 8482 executeNextState(nextState); 8483 setRunning(false); 8484 } 8485 } 8486 8487 } 8488 8489 class EngineThread implements Runnable { 8490 FullRestoreEngine mEngine; 8491 FileInputStream mEngineStream; 8492 8493 EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) { 8494 mEngine = engine; 8495 engine.setRunning(true); 8496 mEngineStream = new FileInputStream(engineSocket.getFileDescriptor()); 8497 } 8498 8499 public boolean isRunning() { 8500 return mEngine.isRunning(); 8501 } 8502 8503 public int waitForResult() { 8504 return mEngine.waitForResult(); 8505 } 8506 8507 @Override 8508 public void run() { 8509 while (mEngine.isRunning()) { 8510 // Tell it to be sure to leave the agent instance up after finishing 8511 mEngine.restoreOneFile(mEngineStream, false); 8512 } 8513 } 8514 } 8515 8516 // state FINAL : tear everything down and we're done. 8517 private void finalizeRestore() { 8518 if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver); 8519 8520 try { 8521 mTransport.finishRestore(); 8522 } catch (Exception e) { 8523 Slog.e(TAG, "Error finishing restore", e); 8524 } 8525 8526 // Tell the observer we're done 8527 if (mObserver != null) { 8528 try { 8529 mObserver.restoreFinished(mStatus); 8530 } catch (RemoteException e) { 8531 Slog.d(TAG, "Restore observer died at restoreFinished"); 8532 } 8533 } 8534 8535 // Clear any ongoing session timeout. 8536 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 8537 8538 // If we have a PM token, we must under all circumstances be sure to 8539 // handshake when we've finished. 8540 if (mPmToken > 0) { 8541 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken); 8542 try { 8543 mPackageManagerBinder.finishPackageInstall(mPmToken, mDidLaunch); 8544 } catch (RemoteException e) { /* can't happen */ } 8545 } else { 8546 // We were invoked via an active restore session, not by the Package 8547 // Manager, so start up the session timeout again. 8548 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, 8549 TIMEOUT_RESTORE_INTERVAL); 8550 } 8551 8552 // Kick off any work that may be needed regarding app widget restores 8553 // TODO: http://b/22388012 8554 AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM); 8555 8556 // If this was a full-system restore, record the ancestral 8557 // dataset information 8558 if (mIsSystemRestore && mPmAgent != null) { 8559 mAncestralPackages = mPmAgent.getRestoredPackages(); 8560 mAncestralToken = mToken; 8561 writeRestoreTokens(); 8562 } 8563 8564 // done; we can finally release the wakelock and be legitimately done. 8565 Slog.i(TAG, "Restore complete."); 8566 mWakelock.release(); 8567 } 8568 8569 void keyValueAgentErrorCleanup() { 8570 // If the agent fails restore, it might have put the app's data 8571 // into an incoherent state. For consistency we wipe its data 8572 // again in this case before continuing with normal teardown 8573 clearApplicationDataSynchronous(mCurrentPackage.packageName); 8574 keyValueAgentCleanup(); 8575 } 8576 8577 // TODO: clean up naming; this is now used at finish by both k/v and stream restores 8578 void keyValueAgentCleanup() { 8579 mBackupDataName.delete(); 8580 mStageName.delete(); 8581 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 8582 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 8583 mBackupData = mNewState = null; 8584 8585 // if everything went okay, remember the recorded state now 8586 // 8587 // !!! TODO: the restored data could be migrated on the server 8588 // side into the current dataset. In that case the new state file 8589 // we just created would reflect the data already extant in the 8590 // backend, so there'd be nothing more to do. Until that happens, 8591 // however, we need to make sure that we record the data to the 8592 // current backend dataset. (Yes, this means shipping the data over 8593 // the wire in both directions. That's bad, but consistency comes 8594 // first, then efficiency.) Once we introduce server-side data 8595 // migration to the newly-restored device's dataset, we will change 8596 // the following from a discard of the newly-written state to the 8597 // "correct" operation of renaming into the canonical state blob. 8598 mNewStateName.delete(); // TODO: remove; see above comment 8599 //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this 8600 8601 // If this wasn't the PM pseudopackage, tear down the agent side 8602 if (mCurrentPackage.applicationInfo != null) { 8603 // unbind and tidy up even on timeout or failure 8604 try { 8605 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 8606 8607 // The agent was probably running with a stub Application object, 8608 // which isn't a valid run mode for the main app logic. Shut 8609 // down the app so that next time it's launched, it gets the 8610 // usual full initialization. Note that this is only done for 8611 // full-system restores: when a single app has requested a restore, 8612 // it is explicitly not killed following that operation. 8613 // 8614 // We execute this kill when these conditions hold: 8615 // 1. it's not a system-uid process, 8616 // 2. the app did not request its own restore (mTargetPackage == null), and either 8617 // 3a. the app is a full-data target (TYPE_FULL_STREAM) or 8618 // b. the app does not state android:killAfterRestore="false" in its manifest 8619 final int appFlags = mCurrentPackage.applicationInfo.flags; 8620 final boolean killAfterRestore = 8621 (mCurrentPackage.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 8622 && ((mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM) 8623 || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0)); 8624 8625 if (mTargetPackage == null && killAfterRestore) { 8626 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of " 8627 + mCurrentPackage.applicationInfo.processName); 8628 mActivityManager.killApplicationProcess( 8629 mCurrentPackage.applicationInfo.processName, 8630 mCurrentPackage.applicationInfo.uid); 8631 } 8632 } catch (RemoteException e) { 8633 // can't happen; we run in the same process as the activity manager 8634 } 8635 } 8636 8637 // The caller is responsible for reestablishing the state machine; our 8638 // responsibility here is to clear the decks for whatever comes next. 8639 mBackupHandler.removeMessages(MSG_TIMEOUT, this); 8640 synchronized (mCurrentOpLock) { 8641 mCurrentOperations.clear(); 8642 } 8643 } 8644 8645 @Override 8646 public void operationComplete(long unusedResult) { 8647 if (MORE_DEBUG) { 8648 Slog.i(TAG, "operationComplete() during restore: target=" 8649 + mCurrentPackage.packageName 8650 + " state=" + mState); 8651 } 8652 8653 final UnifiedRestoreState nextState; 8654 switch (mState) { 8655 case INITIAL: 8656 // We've just (manually) restored the PMBA. It doesn't need the 8657 // additional restore-finished callback so we bypass that and go 8658 // directly to running the queue. 8659 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8660 break; 8661 8662 case RESTORE_KEYVALUE: 8663 case RESTORE_FULL: { 8664 // Okay, we've just heard back from the agent that it's done with 8665 // the restore itself. We now have to send the same agent its 8666 // doRestoreFinished() callback, so roll into that state. 8667 nextState = UnifiedRestoreState.RESTORE_FINISHED; 8668 break; 8669 } 8670 8671 case RESTORE_FINISHED: { 8672 // Okay, we're done with this package. Tidy up and go on to the next 8673 // app in the queue. 8674 int size = (int) mBackupDataName.length(); 8675 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, 8676 mCurrentPackage.packageName, size); 8677 8678 // Just go back to running the restore queue 8679 keyValueAgentCleanup(); 8680 8681 // If there was widget state associated with this app, get the OS to 8682 // incorporate it into current bookeeping and then pass that along to 8683 // the app as part of the restore-time work. 8684 if (mWidgetData != null) { 8685 restoreWidgetData(mCurrentPackage.packageName, mWidgetData); 8686 } 8687 8688 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8689 break; 8690 } 8691 8692 default: { 8693 // Some kind of horrible semantic error; we're in an unexpected state. 8694 // Back off hard and wind up. 8695 Slog.e(TAG, "Unexpected restore callback into state " + mState); 8696 keyValueAgentErrorCleanup(); 8697 nextState = UnifiedRestoreState.FINAL; 8698 break; 8699 } 8700 } 8701 8702 executeNextState(nextState); 8703 } 8704 8705 // A call to agent.doRestore() or agent.doRestoreFinished() has timed out 8706 @Override 8707 public void handleTimeout() { 8708 Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName); 8709 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8710 mCurrentPackage.packageName, "restore timeout"); 8711 // Handle like an agent that threw on invocation: wipe it and go on to the next 8712 keyValueAgentErrorCleanup(); 8713 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8714 } 8715 8716 void executeNextState(UnifiedRestoreState nextState) { 8717 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 8718 + this + " nextState=" + nextState); 8719 mState = nextState; 8720 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 8721 mBackupHandler.sendMessage(msg); 8722 } 8723 8724 // restore observer support 8725 void sendStartRestore(int numPackages) { 8726 if (mObserver != null) { 8727 try { 8728 mObserver.restoreStarting(numPackages); 8729 } catch (RemoteException e) { 8730 Slog.w(TAG, "Restore observer went away: startRestore"); 8731 mObserver = null; 8732 } 8733 } 8734 } 8735 8736 void sendOnRestorePackage(String name) { 8737 if (mObserver != null) { 8738 if (mObserver != null) { 8739 try { 8740 mObserver.onUpdate(mCount, name); 8741 } catch (RemoteException e) { 8742 Slog.d(TAG, "Restore observer died in onUpdate"); 8743 mObserver = null; 8744 } 8745 } 8746 } 8747 } 8748 8749 void sendEndRestore() { 8750 if (mObserver != null) { 8751 try { 8752 mObserver.restoreFinished(mStatus); 8753 } catch (RemoteException e) { 8754 Slog.w(TAG, "Restore observer went away: endRestore"); 8755 mObserver = null; 8756 } 8757 } 8758 } 8759 } 8760 8761 class PerformClearTask implements Runnable { 8762 IBackupTransport mTransport; 8763 PackageInfo mPackage; 8764 8765 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) { 8766 mTransport = transport; 8767 mPackage = packageInfo; 8768 } 8769 8770 public void run() { 8771 try { 8772 // Clear the on-device backup state to ensure a full backup next time 8773 File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); 8774 File stateFile = new File(stateDir, mPackage.packageName); 8775 stateFile.delete(); 8776 8777 // Tell the transport to remove all the persistent storage for the app 8778 // TODO - need to handle failures 8779 mTransport.clearBackupData(mPackage); 8780 } catch (RemoteException e) { 8781 // can't happen; the transport is local 8782 } catch (Exception e) { 8783 Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage); 8784 } finally { 8785 try { 8786 // TODO - need to handle failures 8787 mTransport.finishBackup(); 8788 } catch (RemoteException e) { 8789 // can't happen; the transport is local 8790 } 8791 8792 // Last but not least, release the cpu 8793 mWakelock.release(); 8794 } 8795 } 8796 } 8797 8798 class PerformInitializeTask implements Runnable { 8799 HashSet<String> mQueue; 8800 8801 PerformInitializeTask(HashSet<String> transportNames) { 8802 mQueue = transportNames; 8803 } 8804 8805 public void run() { 8806 try { 8807 for (String transportName : mQueue) { 8808 IBackupTransport transport = getTransport(transportName); 8809 if (transport == null) { 8810 Slog.e(TAG, "Requested init for " + transportName + " but not found"); 8811 continue; 8812 } 8813 8814 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); 8815 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName()); 8816 long startRealtime = SystemClock.elapsedRealtime(); 8817 int status = transport.initializeDevice(); 8818 8819 if (status == BackupTransport.TRANSPORT_OK) { 8820 status = transport.finishBackup(); 8821 } 8822 8823 // Okay, the wipe really happened. Clean up our local bookkeeping. 8824 if (status == BackupTransport.TRANSPORT_OK) { 8825 Slog.i(TAG, "Device init successful"); 8826 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); 8827 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 8828 resetBackupState(new File(mBaseStateDir, transport.transportDirName())); 8829 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); 8830 synchronized (mQueueLock) { 8831 recordInitPendingLocked(false, transportName); 8832 } 8833 } else { 8834 // If this didn't work, requeue this one and try again 8835 // after a suitable interval 8836 Slog.e(TAG, "Transport error in initializeDevice()"); 8837 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 8838 synchronized (mQueueLock) { 8839 recordInitPendingLocked(true, transportName); 8840 } 8841 // do this via another alarm to make sure of the wakelock states 8842 long delay = transport.requestBackupTime(); 8843 Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay); 8844 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 8845 System.currentTimeMillis() + delay, mRunInitIntent); 8846 } 8847 } 8848 } catch (RemoteException e) { 8849 // can't happen; the transports are local 8850 } catch (Exception e) { 8851 Slog.e(TAG, "Unexpected error performing init", e); 8852 } finally { 8853 // Done; release the wakelock 8854 mWakelock.release(); 8855 } 8856 } 8857 } 8858 8859 private void dataChangedImpl(String packageName) { 8860 HashSet<String> targets = dataChangedTargets(packageName); 8861 dataChangedImpl(packageName, targets); 8862 } 8863 8864 private void dataChangedImpl(String packageName, HashSet<String> targets) { 8865 // Record that we need a backup pass for the caller. Since multiple callers 8866 // may share a uid, we need to note all candidates within that uid and schedule 8867 // a backup pass for each of them. 8868 if (targets == null) { 8869 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 8870 + " uid=" + Binder.getCallingUid()); 8871 return; 8872 } 8873 8874 synchronized (mQueueLock) { 8875 // Note that this client has made data changes that need to be backed up 8876 if (targets.contains(packageName)) { 8877 // Add the caller to the set of pending backups. If there is 8878 // one already there, then overwrite it, but no harm done. 8879 BackupRequest req = new BackupRequest(packageName); 8880 if (mPendingBackups.put(packageName, req) == null) { 8881 if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName); 8882 8883 // Journal this request in case of crash. The put() 8884 // operation returned null when this package was not already 8885 // in the set; we want to avoid touching the disk redundantly. 8886 writeToJournalLocked(packageName); 8887 } 8888 } 8889 } 8890 8891 // ...and schedule a backup pass if necessary 8892 KeyValueBackupJob.schedule(mContext); 8893 } 8894 8895 // Note: packageName is currently unused, but may be in the future 8896 private HashSet<String> dataChangedTargets(String packageName) { 8897 // If the caller does not hold the BACKUP permission, it can only request a 8898 // backup of its own data. 8899 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 8900 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 8901 synchronized (mBackupParticipants) { 8902 return mBackupParticipants.get(Binder.getCallingUid()); 8903 } 8904 } 8905 8906 // a caller with full permission can ask to back up any participating app 8907 HashSet<String> targets = new HashSet<String>(); 8908 if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) { 8909 targets.add(PACKAGE_MANAGER_SENTINEL); 8910 } else { 8911 synchronized (mBackupParticipants) { 8912 int N = mBackupParticipants.size(); 8913 for (int i = 0; i < N; i++) { 8914 HashSet<String> s = mBackupParticipants.valueAt(i); 8915 if (s != null) { 8916 targets.addAll(s); 8917 } 8918 } 8919 } 8920 } 8921 return targets; 8922 } 8923 8924 private void writeToJournalLocked(String str) { 8925 RandomAccessFile out = null; 8926 try { 8927 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir); 8928 out = new RandomAccessFile(mJournal, "rws"); 8929 out.seek(out.length()); 8930 out.writeUTF(str); 8931 } catch (IOException e) { 8932 Slog.e(TAG, "Can't write " + str + " to backup journal", e); 8933 mJournal = null; 8934 } finally { 8935 try { if (out != null) out.close(); } catch (IOException e) {} 8936 } 8937 } 8938 8939 // ----- IBackupManager binder interface ----- 8940 8941 public void dataChanged(final String packageName) { 8942 final int callingUserHandle = UserHandle.getCallingUserId(); 8943 if (callingUserHandle != UserHandle.USER_SYSTEM) { 8944 // TODO: http://b/22388012 8945 // App is running under a non-owner user profile. For now, we do not back 8946 // up data from secondary user profiles. 8947 // TODO: backups for all user profiles although don't add backup for profiles 8948 // without adding admin control in DevicePolicyManager. 8949 if (MORE_DEBUG) { 8950 Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " 8951 + callingUserHandle); 8952 } 8953 return; 8954 } 8955 8956 final HashSet<String> targets = dataChangedTargets(packageName); 8957 if (targets == null) { 8958 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 8959 + " uid=" + Binder.getCallingUid()); 8960 return; 8961 } 8962 8963 mBackupHandler.post(new Runnable() { 8964 public void run() { 8965 dataChangedImpl(packageName, targets); 8966 } 8967 }); 8968 } 8969 8970 // Clear the given package's backup data from the current transport 8971 public void clearBackupData(String transportName, String packageName) { 8972 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); 8973 PackageInfo info; 8974 try { 8975 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 8976 } catch (NameNotFoundException e) { 8977 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); 8978 return; 8979 } 8980 8981 // If the caller does not hold the BACKUP permission, it can only request a 8982 // wipe of its own backed-up data. 8983 HashSet<String> apps; 8984 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 8985 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 8986 apps = mBackupParticipants.get(Binder.getCallingUid()); 8987 } else { 8988 // a caller with full permission can ask to back up any participating app 8989 // !!! TODO: allow data-clear of ANY app? 8990 if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps"); 8991 apps = new HashSet<String>(); 8992 int N = mBackupParticipants.size(); 8993 for (int i = 0; i < N; i++) { 8994 HashSet<String> s = mBackupParticipants.valueAt(i); 8995 if (s != null) { 8996 apps.addAll(s); 8997 } 8998 } 8999 } 9000 9001 // Is the given app an available participant? 9002 if (apps.contains(packageName)) { 9003 // found it; fire off the clear request 9004 if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process"); 9005 mBackupHandler.removeMessages(MSG_RETRY_CLEAR); 9006 synchronized (mQueueLock) { 9007 final IBackupTransport transport = getTransport(transportName); 9008 if (transport == null) { 9009 // transport is currently unavailable -- make sure to retry 9010 Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR, 9011 new ClearRetryParams(transportName, packageName)); 9012 mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL); 9013 return; 9014 } 9015 long oldId = Binder.clearCallingIdentity(); 9016 mWakelock.acquire(); 9017 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, 9018 new ClearParams(transport, info)); 9019 mBackupHandler.sendMessage(msg); 9020 Binder.restoreCallingIdentity(oldId); 9021 } 9022 } 9023 } 9024 9025 // Run a backup pass immediately for any applications that have declared 9026 // that they have pending updates. 9027 public void backupNow() { 9028 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow"); 9029 9030 if (mPowerManager.isPowerSaveMode()) { 9031 if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode"); 9032 KeyValueBackupJob.schedule(mContext); // try again in several hours 9033 } else { 9034 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); 9035 synchronized (mQueueLock) { 9036 // Fire the intent that kicks off the whole shebang... 9037 try { 9038 mRunBackupIntent.send(); 9039 } catch (PendingIntent.CanceledException e) { 9040 // should never happen 9041 Slog.e(TAG, "run-backup intent cancelled!"); 9042 } 9043 9044 // ...and cancel any pending scheduled job, because we've just superseded it 9045 KeyValueBackupJob.cancel(mContext); 9046 } 9047 } 9048 } 9049 9050 boolean deviceIsProvisioned() { 9051 final ContentResolver resolver = mContext.getContentResolver(); 9052 return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); 9053 } 9054 9055 // Run a *full* backup pass for the given packages, writing the resulting data stream 9056 // to the supplied file descriptor. This method is synchronous and does not return 9057 // to the caller until the backup has been completed. 9058 // 9059 // This is the variant used by 'adb backup'; it requires on-screen confirmation 9060 // by the user because it can be used to offload data over untrusted USB. 9061 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, 9062 boolean includeObbs, boolean includeShared, boolean doWidgets, 9063 boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) { 9064 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); 9065 9066 final int callingUserHandle = UserHandle.getCallingUserId(); 9067 // TODO: http://b/22388012 9068 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9069 throw new IllegalStateException("Backup supported only for the device owner"); 9070 } 9071 9072 // Validate 9073 if (!doAllApps) { 9074 if (!includeShared) { 9075 // If we're backing up shared data (sdcard or equivalent), then we can run 9076 // without any supplied app names. Otherwise, we'd be doing no work, so 9077 // report the error. 9078 if (pkgList == null || pkgList.length == 0) { 9079 throw new IllegalArgumentException( 9080 "Backup requested but neither shared nor any apps named"); 9081 } 9082 } 9083 } 9084 9085 long oldId = Binder.clearCallingIdentity(); 9086 try { 9087 // Doesn't make sense to do a full backup prior to setup 9088 if (!deviceIsProvisioned()) { 9089 Slog.i(TAG, "Full backup not supported before setup"); 9090 return; 9091 } 9092 9093 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks 9094 + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps 9095 + " system=" + includeSystem + " pkgs=" + pkgList); 9096 Slog.i(TAG, "Beginning full backup..."); 9097 9098 FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs, 9099 includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList); 9100 final int token = generateToken(); 9101 synchronized (mFullConfirmations) { 9102 mFullConfirmations.put(token, params); 9103 } 9104 9105 // start up the confirmation UI 9106 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token); 9107 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { 9108 Slog.e(TAG, "Unable to launch full backup confirmation"); 9109 mFullConfirmations.delete(token); 9110 return; 9111 } 9112 9113 // make sure the screen is lit for the user interaction 9114 mPowerManager.userActivity(SystemClock.uptimeMillis(), 9115 PowerManager.USER_ACTIVITY_EVENT_OTHER, 9116 0); 9117 9118 // start the confirmation countdown 9119 startConfirmationTimeout(token, params); 9120 9121 // wait for the backup to be performed 9122 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion..."); 9123 waitForCompletion(params); 9124 } finally { 9125 try { 9126 fd.close(); 9127 } catch (IOException e) { 9128 // just eat it 9129 } 9130 Binder.restoreCallingIdentity(oldId); 9131 Slog.d(TAG, "Full backup processing complete."); 9132 } 9133 } 9134 9135 public void fullTransportBackup(String[] pkgNames) { 9136 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, 9137 "fullTransportBackup"); 9138 9139 final int callingUserHandle = UserHandle.getCallingUserId(); 9140 // TODO: http://b/22388012 9141 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9142 throw new IllegalStateException("Restore supported only for the device owner"); 9143 } 9144 9145 if (!fullBackupAllowable(getTransport(mCurrentTransport))) { 9146 Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?"); 9147 } else { 9148 if (DEBUG) { 9149 Slog.d(TAG, "fullTransportBackup()"); 9150 } 9151 9152 final long oldId = Binder.clearCallingIdentity(); 9153 try { 9154 CountDownLatch latch = new CountDownLatch(1); 9155 PerformFullTransportBackupTask task = new PerformFullTransportBackupTask(null, 9156 pkgNames, false, null, latch, null, false /* userInitiated */); 9157 // Acquiring wakelock for PerformFullTransportBackupTask before its start. 9158 mWakelock.acquire(); 9159 (new Thread(task, "full-transport-master")).start(); 9160 do { 9161 try { 9162 latch.await(); 9163 break; 9164 } catch (InterruptedException e) { 9165 // Just go back to waiting for the latch to indicate completion 9166 } 9167 } while (true); 9168 9169 // We just ran a backup on these packages, so kick them to the end of the queue 9170 final long now = System.currentTimeMillis(); 9171 for (String pkg : pkgNames) { 9172 enqueueFullBackup(pkg, now); 9173 } 9174 } finally { 9175 Binder.restoreCallingIdentity(oldId); 9176 } 9177 } 9178 9179 if (DEBUG) { 9180 Slog.d(TAG, "Done with full transport backup."); 9181 } 9182 } 9183 9184 public void fullRestore(ParcelFileDescriptor fd) { 9185 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); 9186 9187 final int callingUserHandle = UserHandle.getCallingUserId(); 9188 // TODO: http://b/22388012 9189 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9190 throw new IllegalStateException("Restore supported only for the device owner"); 9191 } 9192 9193 long oldId = Binder.clearCallingIdentity(); 9194 9195 try { 9196 // Check whether the device has been provisioned -- we don't handle 9197 // full restores prior to completing the setup process. 9198 if (!deviceIsProvisioned()) { 9199 Slog.i(TAG, "Full restore not permitted before setup"); 9200 return; 9201 } 9202 9203 Slog.i(TAG, "Beginning full restore..."); 9204 9205 FullRestoreParams params = new FullRestoreParams(fd); 9206 final int token = generateToken(); 9207 synchronized (mFullConfirmations) { 9208 mFullConfirmations.put(token, params); 9209 } 9210 9211 // start up the confirmation UI 9212 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token); 9213 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) { 9214 Slog.e(TAG, "Unable to launch full restore confirmation"); 9215 mFullConfirmations.delete(token); 9216 return; 9217 } 9218 9219 // make sure the screen is lit for the user interaction 9220 mPowerManager.userActivity(SystemClock.uptimeMillis(), 9221 PowerManager.USER_ACTIVITY_EVENT_OTHER, 9222 0); 9223 9224 // start the confirmation countdown 9225 startConfirmationTimeout(token, params); 9226 9227 // wait for the restore to be performed 9228 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion..."); 9229 waitForCompletion(params); 9230 } finally { 9231 try { 9232 fd.close(); 9233 } catch (IOException e) { 9234 Slog.w(TAG, "Error trying to close fd after full restore: " + e); 9235 } 9236 Binder.restoreCallingIdentity(oldId); 9237 Slog.i(TAG, "Full restore processing complete."); 9238 } 9239 } 9240 9241 boolean startConfirmationUi(int token, String action) { 9242 try { 9243 Intent confIntent = new Intent(action); 9244 confIntent.setClassName("com.android.backupconfirm", 9245 "com.android.backupconfirm.BackupRestoreConfirmation"); 9246 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token); 9247 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 9248 mContext.startActivity(confIntent); 9249 } catch (ActivityNotFoundException e) { 9250 return false; 9251 } 9252 return true; 9253 } 9254 9255 void startConfirmationTimeout(int token, FullParams params) { 9256 if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after " 9257 + TIMEOUT_FULL_CONFIRMATION + " millis"); 9258 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, 9259 token, 0, params); 9260 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION); 9261 } 9262 9263 void waitForCompletion(FullParams params) { 9264 synchronized (params.latch) { 9265 while (params.latch.get() == false) { 9266 try { 9267 params.latch.wait(); 9268 } catch (InterruptedException e) { /* never interrupted */ } 9269 } 9270 } 9271 } 9272 9273 void signalFullBackupRestoreCompletion(FullParams params) { 9274 synchronized (params.latch) { 9275 params.latch.set(true); 9276 params.latch.notifyAll(); 9277 } 9278 } 9279 9280 // Confirm that the previously-requested full backup/restore operation can proceed. This 9281 // is used to require a user-facing disclosure about the operation. 9282 public void acknowledgeFullBackupOrRestore(int token, boolean allow, 9283 String curPassword, String encPpassword, IFullBackupRestoreObserver observer) { 9284 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token 9285 + " allow=" + allow); 9286 9287 // TODO: possibly require not just this signature-only permission, but even 9288 // require that the specific designated confirmation-UI app uid is the caller? 9289 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore"); 9290 9291 long oldId = Binder.clearCallingIdentity(); 9292 try { 9293 9294 FullParams params; 9295 synchronized (mFullConfirmations) { 9296 params = mFullConfirmations.get(token); 9297 if (params != null) { 9298 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params); 9299 mFullConfirmations.delete(token); 9300 9301 if (allow) { 9302 final int verb = params instanceof FullBackupParams 9303 ? MSG_RUN_ADB_BACKUP 9304 : MSG_RUN_ADB_RESTORE; 9305 9306 params.observer = observer; 9307 params.curPassword = curPassword; 9308 9309 params.encryptPassword = encPpassword; 9310 9311 if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb); 9312 mWakelock.acquire(); 9313 Message msg = mBackupHandler.obtainMessage(verb, params); 9314 mBackupHandler.sendMessage(msg); 9315 } else { 9316 Slog.w(TAG, "User rejected full backup/restore operation"); 9317 // indicate completion without having actually transferred any data 9318 signalFullBackupRestoreCompletion(params); 9319 } 9320 } else { 9321 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token"); 9322 } 9323 } 9324 } finally { 9325 Binder.restoreCallingIdentity(oldId); 9326 } 9327 } 9328 9329 private static boolean backupSettingMigrated(int userId) { 9330 File base = new File(Environment.getDataDirectory(), "backup"); 9331 File enableFile = new File(base, BACKUP_ENABLE_FILE); 9332 return enableFile.exists(); 9333 } 9334 9335 private static boolean readBackupEnableState(int userId) { 9336 File base = new File(Environment.getDataDirectory(), "backup"); 9337 File enableFile = new File(base, BACKUP_ENABLE_FILE); 9338 if (enableFile.exists()) { 9339 try (FileInputStream fin = new FileInputStream(enableFile)) { 9340 int state = fin.read(); 9341 return state != 0; 9342 } catch (IOException e) { 9343 // can't read the file; fall through to assume disabled 9344 Slog.e(TAG, "Cannot read enable state; assuming disabled"); 9345 } 9346 } else { 9347 if (DEBUG) { 9348 Slog.i(TAG, "isBackupEnabled() => false due to absent settings file"); 9349 } 9350 } 9351 return false; 9352 } 9353 9354 private static void writeBackupEnableState(boolean enable, int userId) { 9355 File base = new File(Environment.getDataDirectory(), "backup"); 9356 File enableFile = new File(base, BACKUP_ENABLE_FILE); 9357 File stage = new File(base, BACKUP_ENABLE_FILE + "-stage"); 9358 FileOutputStream fout = null; 9359 try { 9360 fout = new FileOutputStream(stage); 9361 fout.write(enable ? 1 : 0); 9362 fout.close(); 9363 stage.renameTo(enableFile); 9364 // will be synced immediately by the try-with-resources call to close() 9365 } catch (IOException|RuntimeException e) { 9366 // Whoops; looks like we're doomed. Roll everything out, disabled, 9367 // including the legacy state. 9368 Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: " 9369 + e.getMessage()); 9370 9371 final ContentResolver r = sInstance.mContext.getContentResolver(); 9372 Settings.Secure.putStringForUser(r, 9373 Settings.Secure.BACKUP_ENABLED, null, userId); 9374 enableFile.delete(); 9375 stage.delete(); 9376 } finally { 9377 IoUtils.closeQuietly(fout); 9378 } 9379 } 9380 9381 // Enable/disable backups 9382 public void setBackupEnabled(boolean enable) { 9383 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9384 "setBackupEnabled"); 9385 9386 Slog.i(TAG, "Backup enabled => " + enable); 9387 9388 long oldId = Binder.clearCallingIdentity(); 9389 try { 9390 boolean wasEnabled = mEnabled; 9391 synchronized (this) { 9392 writeBackupEnableState(enable, UserHandle.USER_SYSTEM); 9393 mEnabled = enable; 9394 } 9395 9396 synchronized (mQueueLock) { 9397 if (enable && !wasEnabled && mProvisioned) { 9398 // if we've just been enabled, start scheduling backup passes 9399 KeyValueBackupJob.schedule(mContext); 9400 scheduleNextFullBackupJob(0); 9401 } else if (!enable) { 9402 // No longer enabled, so stop running backups 9403 if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup"); 9404 9405 KeyValueBackupJob.cancel(mContext); 9406 9407 // This also constitutes an opt-out, so we wipe any data for 9408 // this device from the backend. We start that process with 9409 // an alarm in order to guarantee wakelock states. 9410 if (wasEnabled && mProvisioned) { 9411 // NOTE: we currently flush every registered transport, not just 9412 // the currently-active one. 9413 HashSet<String> allTransports; 9414 synchronized (mTransports) { 9415 allTransports = new HashSet<String>(mTransports.keySet()); 9416 } 9417 // build the set of transports for which we are posting an init 9418 for (String transport : allTransports) { 9419 recordInitPendingLocked(true, transport); 9420 } 9421 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 9422 mRunInitIntent); 9423 } 9424 } 9425 } 9426 } finally { 9427 Binder.restoreCallingIdentity(oldId); 9428 } 9429 } 9430 9431 // Enable/disable automatic restore of app data at install time 9432 public void setAutoRestore(boolean doAutoRestore) { 9433 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9434 "setAutoRestore"); 9435 9436 Slog.i(TAG, "Auto restore => " + doAutoRestore); 9437 9438 final long oldId = Binder.clearCallingIdentity(); 9439 try { 9440 synchronized (this) { 9441 Settings.Secure.putInt(mContext.getContentResolver(), 9442 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0); 9443 mAutoRestore = doAutoRestore; 9444 } 9445 } finally { 9446 Binder.restoreCallingIdentity(oldId); 9447 } 9448 } 9449 9450 // Mark the backup service as having been provisioned 9451 public void setBackupProvisioned(boolean available) { 9452 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9453 "setBackupProvisioned"); 9454 /* 9455 * This is now a no-op; provisioning is simply the device's own setup state. 9456 */ 9457 } 9458 9459 // Report whether the backup mechanism is currently enabled 9460 public boolean isBackupEnabled() { 9461 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); 9462 return mEnabled; // no need to synchronize just to read it 9463 } 9464 9465 // Report the name of the currently active transport 9466 public String getCurrentTransport() { 9467 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9468 "getCurrentTransport"); 9469 if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); 9470 return mCurrentTransport; 9471 } 9472 9473 // Report all known, available backup transports 9474 public String[] listAllTransports() { 9475 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); 9476 9477 String[] list = null; 9478 ArrayList<String> known = new ArrayList<String>(); 9479 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { 9480 if (entry.getValue() != null) { 9481 known.add(entry.getKey()); 9482 } 9483 } 9484 9485 if (known.size() > 0) { 9486 list = new String[known.size()]; 9487 known.toArray(list); 9488 } 9489 return list; 9490 } 9491 9492 // Select which transport to use for the next backup operation. 9493 public String selectBackupTransport(String transport) { 9494 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9495 "selectBackupTransport"); 9496 9497 synchronized (mTransports) { 9498 final long oldId = Binder.clearCallingIdentity(); 9499 try { 9500 String prevTransport = mCurrentTransport; 9501 mCurrentTransport = transport; 9502 Settings.Secure.putString(mContext.getContentResolver(), 9503 Settings.Secure.BACKUP_TRANSPORT, transport); 9504 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport 9505 + " returning " + prevTransport); 9506 return prevTransport; 9507 } finally { 9508 Binder.restoreCallingIdentity(oldId); 9509 } 9510 } 9511 } 9512 9513 // Supply the configuration Intent for the given transport. If the name is not one 9514 // of the available transports, or if the transport does not supply any configuration 9515 // UI, the method returns null. 9516 public Intent getConfigurationIntent(String transportName) { 9517 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9518 "getConfigurationIntent"); 9519 9520 synchronized (mTransports) { 9521 final IBackupTransport transport = mTransports.get(transportName); 9522 if (transport != null) { 9523 try { 9524 final Intent intent = transport.configurationIntent(); 9525 if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent " 9526 + intent); 9527 return intent; 9528 } catch (RemoteException e) { 9529 /* fall through to return null */ 9530 } 9531 } 9532 } 9533 9534 return null; 9535 } 9536 9537 // Supply the configuration summary string for the given transport. If the name is 9538 // not one of the available transports, or if the transport does not supply any 9539 // summary / destination string, the method can return null. 9540 // 9541 // This string is used VERBATIM as the summary text of the relevant Settings item! 9542 public String getDestinationString(String transportName) { 9543 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9544 "getDestinationString"); 9545 9546 synchronized (mTransports) { 9547 final IBackupTransport transport = mTransports.get(transportName); 9548 if (transport != null) { 9549 try { 9550 final String text = transport.currentDestinationString(); 9551 if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text); 9552 return text; 9553 } catch (RemoteException e) { 9554 /* fall through to return null */ 9555 } 9556 } 9557 } 9558 9559 return null; 9560 } 9561 9562 // Supply the manage-data intent for the given transport. 9563 public Intent getDataManagementIntent(String transportName) { 9564 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9565 "getDataManagementIntent"); 9566 9567 synchronized (mTransports) { 9568 final IBackupTransport transport = mTransports.get(transportName); 9569 if (transport != null) { 9570 try { 9571 final Intent intent = transport.dataManagementIntent(); 9572 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent " 9573 + intent); 9574 return intent; 9575 } catch (RemoteException e) { 9576 /* fall through to return null */ 9577 } 9578 } 9579 } 9580 9581 return null; 9582 } 9583 9584 // Supply the menu label for affordances that fire the manage-data intent 9585 // for the given transport. 9586 public String getDataManagementLabel(String transportName) { 9587 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9588 "getDataManagementLabel"); 9589 9590 synchronized (mTransports) { 9591 final IBackupTransport transport = mTransports.get(transportName); 9592 if (transport != null) { 9593 try { 9594 final String text = transport.dataManagementLabel(); 9595 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text); 9596 return text; 9597 } catch (RemoteException e) { 9598 /* fall through to return null */ 9599 } 9600 } 9601 } 9602 9603 return null; 9604 } 9605 9606 // Callback: a requested backup agent has been instantiated. This should only 9607 // be called from the Activity Manager. 9608 public void agentConnected(String packageName, IBinder agentBinder) { 9609 synchronized(mAgentConnectLock) { 9610 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 9611 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); 9612 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); 9613 mConnectedAgent = agent; 9614 mConnecting = false; 9615 } else { 9616 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9617 + " claiming agent connected"); 9618 } 9619 mAgentConnectLock.notifyAll(); 9620 } 9621 } 9622 9623 // Callback: a backup agent has failed to come up, or has unexpectedly quit. 9624 // If the agent failed to come up in the first place, the agentBinder argument 9625 // will be null. This should only be called from the Activity Manager. 9626 public void agentDisconnected(String packageName) { 9627 // TODO: handle backup being interrupted 9628 synchronized(mAgentConnectLock) { 9629 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 9630 mConnectedAgent = null; 9631 mConnecting = false; 9632 } else { 9633 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9634 + " claiming agent disconnected"); 9635 } 9636 mAgentConnectLock.notifyAll(); 9637 } 9638 } 9639 9640 // An application being installed will need a restore pass, then the Package Manager 9641 // will need to be told when the restore is finished. 9642 public void restoreAtInstall(String packageName, int token) { 9643 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 9644 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9645 + " attemping install-time restore"); 9646 return; 9647 } 9648 9649 boolean skip = false; 9650 9651 long restoreSet = getAvailableRestoreToken(packageName); 9652 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName 9653 + " token=" + Integer.toHexString(token) 9654 + " restoreSet=" + Long.toHexString(restoreSet)); 9655 if (restoreSet == 0) { 9656 if (MORE_DEBUG) Slog.i(TAG, "No restore set"); 9657 skip = true; 9658 } 9659 9660 // Do we have a transport to fetch data for us? 9661 IBackupTransport transport = getTransport(mCurrentTransport); 9662 if (transport == null) { 9663 if (DEBUG) Slog.w(TAG, "No transport"); 9664 skip = true; 9665 } 9666 9667 if (!mAutoRestore) { 9668 if (DEBUG) { 9669 Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore); 9670 } 9671 skip = true; 9672 } 9673 9674 if (!skip) { 9675 try { 9676 // okay, we're going to attempt a restore of this package from this restore set. 9677 // The eventual message back into the Package Manager to run the post-install 9678 // steps for 'token' will be issued from the restore handling code. 9679 9680 // This can throw and so *must* happen before the wakelock is acquired 9681 String dirName = transport.transportDirName(); 9682 9683 mWakelock.acquire(); 9684 if (MORE_DEBUG) { 9685 Slog.d(TAG, "Restore at install of " + packageName); 9686 } 9687 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9688 msg.obj = new RestoreParams(transport, dirName, null, 9689 restoreSet, packageName, token); 9690 mBackupHandler.sendMessage(msg); 9691 } catch (RemoteException e) { 9692 // Binding to the transport broke; back off and proceed with the installation. 9693 Slog.e(TAG, "Unable to contact transport"); 9694 skip = true; 9695 } 9696 } 9697 9698 if (skip) { 9699 // Auto-restore disabled or no way to attempt a restore; just tell the Package 9700 // Manager to proceed with the post-install handling for this package. 9701 if (DEBUG) Slog.v(TAG, "Finishing install immediately"); 9702 try { 9703 mPackageManagerBinder.finishPackageInstall(token, false); 9704 } catch (RemoteException e) { /* can't happen */ } 9705 } 9706 } 9707 9708 // Hand off a restore session 9709 public IRestoreSession beginRestoreSession(String packageName, String transport) { 9710 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName 9711 + " transport=" + transport); 9712 9713 boolean needPermission = true; 9714 if (transport == null) { 9715 transport = mCurrentTransport; 9716 9717 if (packageName != null) { 9718 PackageInfo app = null; 9719 try { 9720 app = mPackageManager.getPackageInfo(packageName, 0); 9721 } catch (NameNotFoundException nnf) { 9722 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 9723 throw new IllegalArgumentException("Package " + packageName + " not found"); 9724 } 9725 9726 if (app.applicationInfo.uid == Binder.getCallingUid()) { 9727 // So: using the current active transport, and the caller has asked 9728 // that its own package will be restored. In this narrow use case 9729 // we do not require the caller to hold the permission. 9730 needPermission = false; 9731 } 9732 } 9733 } 9734 9735 if (needPermission) { 9736 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9737 "beginRestoreSession"); 9738 } else { 9739 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed"); 9740 } 9741 9742 synchronized(this) { 9743 if (mActiveRestoreSession != null) { 9744 Slog.d(TAG, "Restore session requested but one already active"); 9745 return null; 9746 } 9747 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport); 9748 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 9749 } 9750 return mActiveRestoreSession; 9751 } 9752 9753 void clearRestoreSession(ActiveRestoreSession currentSession) { 9754 synchronized(this) { 9755 if (currentSession != mActiveRestoreSession) { 9756 Slog.e(TAG, "ending non-current restore session"); 9757 } else { 9758 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout"); 9759 mActiveRestoreSession = null; 9760 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9761 } 9762 } 9763 } 9764 9765 // Note that a currently-active backup agent has notified us that it has 9766 // completed the given outstanding asynchronous backup/restore operation. 9767 public void opComplete(int token, long result) { 9768 if (MORE_DEBUG) { 9769 Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result); 9770 } 9771 Operation op = null; 9772 synchronized (mCurrentOpLock) { 9773 op = mCurrentOperations.get(token); 9774 if (op != null) { 9775 op.state = OP_ACKNOWLEDGED; 9776 } 9777 mCurrentOpLock.notifyAll(); 9778 } 9779 9780 // The completion callback, if any, is invoked on the handler 9781 if (op != null && op.callback != null) { 9782 Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result); 9783 Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult); 9784 mBackupHandler.sendMessage(msg); 9785 } 9786 } 9787 9788 public boolean isAppEligibleForBackup(String packageName) { 9789 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9790 "isAppEligibleForBackup"); 9791 try { 9792 PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 9793 PackageManager.GET_SIGNATURES); 9794 if (!appIsEligibleForBackup(packageInfo.applicationInfo) || 9795 appIsStopped(packageInfo.applicationInfo)) { 9796 return false; 9797 } 9798 IBackupTransport transport = getTransport(mCurrentTransport); 9799 if (transport != null) { 9800 try { 9801 return transport.isAppEligibleForBackup(packageInfo, 9802 appGetsFullBackup(packageInfo)); 9803 } catch (RemoteException e) { 9804 Slog.e(TAG, "Unable to contact transport"); 9805 } 9806 } 9807 // If transport is not present we couldn't tell that the package is not eligible. 9808 return true; 9809 } catch (NameNotFoundException e) { 9810 return false; 9811 } 9812 } 9813 9814 // ----- Restore session ----- 9815 9816 class ActiveRestoreSession extends IRestoreSession.Stub { 9817 private static final String TAG = "RestoreSession"; 9818 9819 private String mPackageName; 9820 private IBackupTransport mRestoreTransport = null; 9821 RestoreSet[] mRestoreSets = null; 9822 boolean mEnded = false; 9823 boolean mTimedOut = false; 9824 9825 ActiveRestoreSession(String packageName, String transport) { 9826 mPackageName = packageName; 9827 mRestoreTransport = getTransport(transport); 9828 } 9829 9830 public void markTimedOut() { 9831 mTimedOut = true; 9832 } 9833 9834 // --- Binder interface --- 9835 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) { 9836 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9837 "getAvailableRestoreSets"); 9838 if (observer == null) { 9839 throw new IllegalArgumentException("Observer must not be null"); 9840 } 9841 9842 if (mEnded) { 9843 throw new IllegalStateException("Restore session already ended"); 9844 } 9845 9846 if (mTimedOut) { 9847 Slog.i(TAG, "Session already timed out"); 9848 return -1; 9849 } 9850 9851 long oldId = Binder.clearCallingIdentity(); 9852 try { 9853 if (mRestoreTransport == null) { 9854 Slog.w(TAG, "Null transport getting restore sets"); 9855 return -1; 9856 } 9857 9858 // We know we're doing legit work now, so halt the timeout 9859 // until we're done. It gets started again when the result 9860 // comes in. 9861 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9862 9863 // spin off the transport request to our service thread 9864 mWakelock.acquire(); 9865 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS, 9866 new RestoreGetSetsParams(mRestoreTransport, this, observer)); 9867 mBackupHandler.sendMessage(msg); 9868 return 0; 9869 } catch (Exception e) { 9870 Slog.e(TAG, "Error in getAvailableRestoreSets", e); 9871 return -1; 9872 } finally { 9873 Binder.restoreCallingIdentity(oldId); 9874 } 9875 } 9876 9877 public synchronized int restoreAll(long token, IRestoreObserver observer) { 9878 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9879 "performRestore"); 9880 9881 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token) 9882 + " observer=" + observer); 9883 9884 if (mEnded) { 9885 throw new IllegalStateException("Restore session already ended"); 9886 } 9887 9888 if (mTimedOut) { 9889 Slog.i(TAG, "Session already timed out"); 9890 return -1; 9891 } 9892 9893 if (mRestoreTransport == null || mRestoreSets == null) { 9894 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 9895 return -1; 9896 } 9897 9898 if (mPackageName != null) { 9899 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 9900 return -1; 9901 } 9902 9903 String dirName; 9904 try { 9905 dirName = mRestoreTransport.transportDirName(); 9906 } catch (RemoteException e) { 9907 // Transport went AWOL; fail. 9908 Slog.e(TAG, "Unable to contact transport for restore"); 9909 return -1; 9910 } 9911 9912 synchronized (mQueueLock) { 9913 for (int i = 0; i < mRestoreSets.length; i++) { 9914 if (token == mRestoreSets[i].token) { 9915 // Real work, so stop the session timeout until we finalize the restore 9916 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9917 9918 long oldId = Binder.clearCallingIdentity(); 9919 mWakelock.acquire(); 9920 if (MORE_DEBUG) { 9921 Slog.d(TAG, "restoreAll() kicking off"); 9922 } 9923 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9924 msg.obj = new RestoreParams(mRestoreTransport, dirName, 9925 observer, token); 9926 mBackupHandler.sendMessage(msg); 9927 Binder.restoreCallingIdentity(oldId); 9928 return 0; 9929 } 9930 } 9931 } 9932 9933 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 9934 return -1; 9935 } 9936 9937 // Restores of more than a single package are treated as 'system' restores 9938 public synchronized int restoreSome(long token, IRestoreObserver observer, 9939 String[] packages) { 9940 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9941 "performRestore"); 9942 9943 if (DEBUG) { 9944 StringBuilder b = new StringBuilder(128); 9945 b.append("restoreSome token="); 9946 b.append(Long.toHexString(token)); 9947 b.append(" observer="); 9948 b.append(observer.toString()); 9949 b.append(" packages="); 9950 if (packages == null) { 9951 b.append("null"); 9952 } else { 9953 b.append('{'); 9954 boolean first = true; 9955 for (String s : packages) { 9956 if (!first) { 9957 b.append(", "); 9958 } else first = false; 9959 b.append(s); 9960 } 9961 b.append('}'); 9962 } 9963 Slog.d(TAG, b.toString()); 9964 } 9965 9966 if (mEnded) { 9967 throw new IllegalStateException("Restore session already ended"); 9968 } 9969 9970 if (mTimedOut) { 9971 Slog.i(TAG, "Session already timed out"); 9972 return -1; 9973 } 9974 9975 if (mRestoreTransport == null || mRestoreSets == null) { 9976 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 9977 return -1; 9978 } 9979 9980 if (mPackageName != null) { 9981 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 9982 return -1; 9983 } 9984 9985 String dirName; 9986 try { 9987 dirName = mRestoreTransport.transportDirName(); 9988 } catch (RemoteException e) { 9989 // Transport went AWOL; fail. 9990 Slog.e(TAG, "Unable to contact transport for restore"); 9991 return -1; 9992 } 9993 9994 synchronized (mQueueLock) { 9995 for (int i = 0; i < mRestoreSets.length; i++) { 9996 if (token == mRestoreSets[i].token) { 9997 // Stop the session timeout until we finalize the restore 9998 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9999 10000 long oldId = Binder.clearCallingIdentity(); 10001 mWakelock.acquire(); 10002 if (MORE_DEBUG) { 10003 Slog.d(TAG, "restoreSome() of " + packages.length + " packages"); 10004 } 10005 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 10006 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, 10007 packages, packages.length > 1); 10008 mBackupHandler.sendMessage(msg); 10009 Binder.restoreCallingIdentity(oldId); 10010 return 0; 10011 } 10012 } 10013 } 10014 10015 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 10016 return -1; 10017 } 10018 10019 public synchronized int restorePackage(String packageName, IRestoreObserver observer) { 10020 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); 10021 10022 if (mEnded) { 10023 throw new IllegalStateException("Restore session already ended"); 10024 } 10025 10026 if (mTimedOut) { 10027 Slog.i(TAG, "Session already timed out"); 10028 return -1; 10029 } 10030 10031 if (mPackageName != null) { 10032 if (! mPackageName.equals(packageName)) { 10033 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName 10034 + " on session for package " + mPackageName); 10035 return -1; 10036 } 10037 } 10038 10039 PackageInfo app = null; 10040 try { 10041 app = mPackageManager.getPackageInfo(packageName, 0); 10042 } catch (NameNotFoundException nnf) { 10043 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 10044 return -1; 10045 } 10046 10047 // If the caller is not privileged and is not coming from the target 10048 // app's uid, throw a permission exception back to the caller. 10049 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP, 10050 Binder.getCallingPid(), Binder.getCallingUid()); 10051 if ((perm == PackageManager.PERMISSION_DENIED) && 10052 (app.applicationInfo.uid != Binder.getCallingUid())) { 10053 Slog.w(TAG, "restorePackage: bad packageName=" + packageName 10054 + " or calling uid=" + Binder.getCallingUid()); 10055 throw new SecurityException("No permission to restore other packages"); 10056 } 10057 10058 // So far so good; we're allowed to try to restore this package. 10059 long oldId = Binder.clearCallingIdentity(); 10060 try { 10061 // Check whether there is data for it in the current dataset, falling back 10062 // to the ancestral dataset if not. 10063 long token = getAvailableRestoreToken(packageName); 10064 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName 10065 + " token=" + Long.toHexString(token)); 10066 10067 // If we didn't come up with a place to look -- no ancestral dataset and 10068 // the app has never been backed up from this device -- there's nothing 10069 // to do but return failure. 10070 if (token == 0) { 10071 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring"); 10072 return -1; 10073 } 10074 10075 String dirName; 10076 try { 10077 dirName = mRestoreTransport.transportDirName(); 10078 } catch (RemoteException e) { 10079 // Transport went AWOL; fail. 10080 Slog.e(TAG, "Unable to contact transport for restore"); 10081 return -1; 10082 } 10083 10084 // Stop the session timeout until we finalize the restore 10085 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10086 10087 // Ready to go: enqueue the restore request and claim success 10088 mWakelock.acquire(); 10089 if (MORE_DEBUG) { 10090 Slog.d(TAG, "restorePackage() : " + packageName); 10091 } 10092 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 10093 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app); 10094 mBackupHandler.sendMessage(msg); 10095 } finally { 10096 Binder.restoreCallingIdentity(oldId); 10097 } 10098 return 0; 10099 } 10100 10101 // Posted to the handler to tear down a restore session in a cleanly synchronized way 10102 class EndRestoreRunnable implements Runnable { 10103 BackupManagerService mBackupManager; 10104 ActiveRestoreSession mSession; 10105 10106 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) { 10107 mBackupManager = manager; 10108 mSession = session; 10109 } 10110 10111 public void run() { 10112 // clean up the session's bookkeeping 10113 synchronized (mSession) { 10114 mSession.mRestoreTransport = null; 10115 mSession.mEnded = true; 10116 } 10117 10118 // clean up the BackupManagerImpl side of the bookkeeping 10119 // and cancel any pending timeout message 10120 mBackupManager.clearRestoreSession(mSession); 10121 } 10122 } 10123 10124 public synchronized void endRestoreSession() { 10125 if (DEBUG) Slog.d(TAG, "endRestoreSession"); 10126 10127 if (mTimedOut) { 10128 Slog.i(TAG, "Session already timed out"); 10129 return; 10130 } 10131 10132 if (mEnded) { 10133 throw new IllegalStateException("Restore session already ended"); 10134 } 10135 10136 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this)); 10137 } 10138 } 10139 10140 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 10141 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 10142 10143 long identityToken = Binder.clearCallingIdentity(); 10144 try { 10145 if (args != null) { 10146 for (String arg : args) { 10147 if ("-h".equals(arg)) { 10148 pw.println("'dumpsys backup' optional arguments:"); 10149 pw.println(" -h : this help text"); 10150 pw.println(" a[gents] : dump information about defined backup agents"); 10151 return; 10152 } else if ("agents".startsWith(arg)) { 10153 dumpAgents(pw); 10154 return; 10155 } 10156 } 10157 } 10158 dumpInternal(pw); 10159 } finally { 10160 Binder.restoreCallingIdentity(identityToken); 10161 } 10162 } 10163 10164 private void dumpAgents(PrintWriter pw) { 10165 List<PackageInfo> agentPackages = allAgentPackages(); 10166 pw.println("Defined backup agents:"); 10167 for (PackageInfo pkg : agentPackages) { 10168 pw.print(" "); 10169 pw.print(pkg.packageName); pw.println(':'); 10170 pw.print(" "); pw.println(pkg.applicationInfo.backupAgentName); 10171 } 10172 } 10173 10174 private void dumpInternal(PrintWriter pw) { 10175 synchronized (mQueueLock) { 10176 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") 10177 + " / " + (!mProvisioned ? "not " : "") + "provisioned / " 10178 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); 10179 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); 10180 if (mBackupRunning) pw.println("Backup currently running"); 10181 pw.println("Last backup pass started: " + mLastBackupPass 10182 + " (now = " + System.currentTimeMillis() + ')'); 10183 pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled()); 10184 10185 pw.println("Available transports:"); 10186 final String[] transports = listAllTransports(); 10187 if (transports != null) { 10188 for (String t : listAllTransports()) { 10189 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t); 10190 try { 10191 IBackupTransport transport = getTransport(t); 10192 File dir = new File(mBaseStateDir, transport.transportDirName()); 10193 pw.println(" destination: " + transport.currentDestinationString()); 10194 pw.println(" intent: " + transport.configurationIntent()); 10195 for (File f : dir.listFiles()) { 10196 pw.println(" " + f.getName() + " - " + f.length() + " state bytes"); 10197 } 10198 } catch (Exception e) { 10199 Slog.e(TAG, "Error in transport", e); 10200 pw.println(" Error: " + e); 10201 } 10202 } 10203 } 10204 10205 pw.println("Pending init: " + mPendingInits.size()); 10206 for (String s : mPendingInits) { 10207 pw.println(" " + s); 10208 } 10209 10210 if (DEBUG_BACKUP_TRACE) { 10211 synchronized (mBackupTrace) { 10212 if (!mBackupTrace.isEmpty()) { 10213 pw.println("Most recent backup trace:"); 10214 for (String s : mBackupTrace) { 10215 pw.println(" " + s); 10216 } 10217 } 10218 } 10219 } 10220 10221 pw.print("Ancestral: "); pw.println(Long.toHexString(mAncestralToken)); 10222 pw.print("Current: "); pw.println(Long.toHexString(mCurrentToken)); 10223 10224 int N = mBackupParticipants.size(); 10225 pw.println("Participants:"); 10226 for (int i=0; i<N; i++) { 10227 int uid = mBackupParticipants.keyAt(i); 10228 pw.print(" uid: "); 10229 pw.println(uid); 10230 HashSet<String> participants = mBackupParticipants.valueAt(i); 10231 for (String app: participants) { 10232 pw.println(" " + app); 10233 } 10234 } 10235 10236 pw.println("Ancestral packages: " 10237 + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); 10238 if (mAncestralPackages != null) { 10239 for (String pkg : mAncestralPackages) { 10240 pw.println(" " + pkg); 10241 } 10242 } 10243 10244 pw.println("Ever backed up: " + mEverStoredApps.size()); 10245 for (String pkg : mEverStoredApps) { 10246 pw.println(" " + pkg); 10247 } 10248 10249 pw.println("Pending key/value backup: " + mPendingBackups.size()); 10250 for (BackupRequest req : mPendingBackups.values()) { 10251 pw.println(" " + req); 10252 } 10253 10254 pw.println("Full backup queue:" + mFullBackupQueue.size()); 10255 for (FullBackupEntry entry : mFullBackupQueue) { 10256 pw.print(" "); pw.print(entry.lastBackup); 10257 pw.print(" : "); pw.println(entry.packageName); 10258 } 10259 } 10260 } 10261 10262 private static void sendBackupOnUpdate(IBackupObserver observer, String packageName, 10263 BackupProgress progress) { 10264 if (observer != null) { 10265 try { 10266 observer.onUpdate(packageName, progress); 10267 } catch (RemoteException e) { 10268 if (DEBUG) { 10269 Slog.w(TAG, "Backup observer went away: onUpdate"); 10270 } 10271 } 10272 } 10273 } 10274 10275 private static void sendBackupOnPackageResult(IBackupObserver observer, String packageName, 10276 int status) { 10277 if (observer != null) { 10278 try { 10279 observer.onResult(packageName, status); 10280 } catch (RemoteException e) { 10281 if (DEBUG) { 10282 Slog.w(TAG, "Backup observer went away: onResult"); 10283 } 10284 } 10285 } 10286 } 10287 10288 private static void sendBackupFinished(IBackupObserver observer, int status) { 10289 if (observer != null) { 10290 try { 10291 observer.backupFinished(status); 10292 } catch (RemoteException e) { 10293 if (DEBUG) { 10294 Slog.w(TAG, "Backup observer went away: backupFinished"); 10295 } 10296 } 10297 } 10298 } 10299} 10300