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