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