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