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