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