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