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