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