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