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