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