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