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