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