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