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