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