BackupManagerService.java revision a7f038c9c3715bf29aec194bf079bf061502b1d9
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 // Don't proceed unless we have already established package metadata 3942 // for the current dataset via a key/value backup pass. 3943 File stateDir = new File(mBaseStateDir, transport.transportDirName()); 3944 File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL); 3945 if (pmState.length() <= 0) { 3946 Slog.i(TAG, "Full backup requested but dataset not yet initialized " 3947 + "via k/v backup pass; ignoring"); 3948 return; 3949 } 3950 3951 // Set up to send data to the transport 3952 final int N = mPackages.size(); 3953 for (int i = 0; i < N; i++) { 3954 currentPackage = mPackages.get(i); 3955 if (DEBUG) { 3956 Slog.i(TAG, "Initiating full-data transport backup of " 3957 + currentPackage.packageName); 3958 } 3959 EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, 3960 currentPackage.packageName); 3961 3962 transportPipes = ParcelFileDescriptor.createPipe(); 3963 3964 // Tell the transport the data's coming 3965 int result = transport.performFullBackup(currentPackage, 3966 transportPipes[0]); 3967 if (result == BackupTransport.TRANSPORT_OK) { 3968 // The transport has its own copy of the read end of the pipe, 3969 // so close ours now 3970 transportPipes[0].close(); 3971 transportPipes[0] = null; 3972 3973 // Now set up the backup engine / data source end of things 3974 enginePipes = ParcelFileDescriptor.createPipe(); 3975 CountDownLatch runnerLatch = new CountDownLatch(1); 3976 SinglePackageBackupRunner backupRunner = 3977 new SinglePackageBackupRunner(enginePipes[1], currentPackage, 3978 transport, runnerLatch); 3979 // The runner dup'd the pipe half, so we close it here 3980 enginePipes[1].close(); 3981 enginePipes[1] = null; 3982 3983 // Spin off the runner to fetch the app's data and pipe it 3984 // into the engine pipes 3985 (new Thread(backupRunner, "package-backup-bridge")).start(); 3986 3987 // Read data off the engine pipe and pass it to the transport 3988 // pipe until we hit EOD on the input stream. 3989 FileInputStream in = new FileInputStream( 3990 enginePipes[0].getFileDescriptor()); 3991 FileOutputStream out = new FileOutputStream( 3992 transportPipes[1].getFileDescriptor()); 3993 byte[] buffer = new byte[8192]; 3994 int nRead = 0; 3995 do { 3996 if (!mKeepRunning.get()) { 3997 if (DEBUG_SCHEDULING) { 3998 Slog.i(TAG, "Full backup task told to stop"); 3999 } 4000 break; 4001 } 4002 nRead = in.read(buffer); 4003 if (MORE_DEBUG) { 4004 Slog.v(TAG, "in.read(buffer) from app: " + nRead); 4005 } 4006 if (nRead > 0) { 4007 out.write(buffer, 0, nRead); 4008 result = transport.sendBackupData(nRead); 4009 } 4010 } while (nRead > 0 && result == BackupTransport.TRANSPORT_OK); 4011 4012 // If we've lost our running criteria, tell the transport to cancel 4013 // and roll back this (partial) backup payload; otherwise tell it 4014 // that we've reached the clean finish state. 4015 if (!mKeepRunning.get()) { 4016 result = BackupTransport.TRANSPORT_ERROR; 4017 transport.cancelFullBackup(); 4018 } else { 4019 // If we were otherwise in a good state, now interpret the final 4020 // result based on what finishBackup() returns. If we're in a 4021 // failure case already, preserve that result and ignore whatever 4022 // finishBackup() reports. 4023 final int finishResult = transport.finishBackup(); 4024 if (result == BackupTransport.TRANSPORT_OK) { 4025 result = finishResult; 4026 } 4027 } 4028 4029 if (MORE_DEBUG) { 4030 Slog.i(TAG, "Done trying to send backup data: result=" + result); 4031 } 4032 4033 if (result != BackupTransport.TRANSPORT_OK) { 4034 Slog.e(TAG, "Error " + result 4035 + " backing up " + currentPackage.packageName); 4036 } 4037 4038 // Also ask the transport how long it wants us to wait before 4039 // moving on to the next package, if any. 4040 backoff = transport.requestFullBackupTime(); 4041 if (DEBUG_SCHEDULING) { 4042 Slog.i(TAG, "Transport suggested backoff=" + backoff); 4043 } 4044 4045 } 4046 4047 // Roll this package to the end of the backup queue if we're 4048 // in a queue-driven mode (regardless of success/failure) 4049 if (mUpdateSchedule) { 4050 enqueueFullBackup(currentPackage.packageName, 4051 System.currentTimeMillis()); 4052 } 4053 4054 if (result == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 4055 if (DEBUG) { 4056 Slog.i(TAG, "Transport rejected backup of " 4057 + currentPackage.packageName 4058 + ", skipping"); 4059 } 4060 EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, 4061 currentPackage.packageName, "transport rejected"); 4062 // do nothing, clean up, and continue looping 4063 } else if (result != BackupTransport.TRANSPORT_OK) { 4064 if (DEBUG) { 4065 Slog.i(TAG, "Transport failed; aborting backup: " + result); 4066 EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE); 4067 return; 4068 } 4069 } else { 4070 // Success! 4071 EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, 4072 currentPackage.packageName); 4073 logBackupComplete(currentPackage.packageName); 4074 } 4075 cleanUpPipes(transportPipes); 4076 cleanUpPipes(enginePipes); 4077 currentPackage = null; 4078 } 4079 4080 if (DEBUG) { 4081 Slog.i(TAG, "Full backup completed."); 4082 } 4083 } catch (Exception e) { 4084 Slog.w(TAG, "Exception trying full transport backup", e); 4085 } finally { 4086 cleanUpPipes(transportPipes); 4087 cleanUpPipes(enginePipes); 4088 4089 if (mJob != null) { 4090 mJob.finishBackupPass(); 4091 } 4092 4093 synchronized (mQueueLock) { 4094 mRunningFullBackupTask = null; 4095 } 4096 4097 mLatch.countDown(); 4098 4099 // Now that we're actually done with schedule-driven work, reschedule 4100 // the next pass based on the new queue state. 4101 if (mUpdateSchedule) { 4102 scheduleNextFullBackupJob(backoff); 4103 } 4104 } 4105 } 4106 4107 void cleanUpPipes(ParcelFileDescriptor[] pipes) { 4108 if (pipes != null) { 4109 if (pipes[0] != null) { 4110 ParcelFileDescriptor fd = pipes[0]; 4111 pipes[0] = null; 4112 try { 4113 fd.close(); 4114 } catch (IOException e) { 4115 Slog.w(TAG, "Unable to close pipe!"); 4116 } 4117 } 4118 if (pipes[1] != null) { 4119 ParcelFileDescriptor fd = pipes[1]; 4120 pipes[1] = null; 4121 try { 4122 fd.close(); 4123 } catch (IOException e) { 4124 Slog.w(TAG, "Unable to close pipe!"); 4125 } 4126 } 4127 } 4128 } 4129 4130 // Run the backup and pipe it back to the given socket -- expects to run on 4131 // a standalone thread. The runner owns this half of the pipe, and closes 4132 // it to indicate EOD to the other end. 4133 class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight { 4134 final AtomicInteger mResult = new AtomicInteger(); 4135 final CountDownLatch mLatch = new CountDownLatch(1); 4136 final IBackupTransport mTransport; 4137 4138 public SinglePackageBackupPreflight(IBackupTransport transport) { 4139 mTransport = transport; 4140 } 4141 4142 @Override 4143 public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) { 4144 int result; 4145 try { 4146 final int token = generateToken(); 4147 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this); 4148 addBackupTrace("preflighting"); 4149 if (MORE_DEBUG) { 4150 Slog.d(TAG, "Preflighting full payload of " + pkg.packageName); 4151 } 4152 agent.doMeasureFullBackup(token, mBackupManagerBinder); 4153 4154 // now wait to get our result back 4155 mLatch.await(); 4156 int totalSize = mResult.get(); 4157 if (MORE_DEBUG) { 4158 Slog.v(TAG, "Got preflight response; size=" + totalSize); 4159 } 4160 4161 result = mTransport.checkFullBackupSize(totalSize); 4162 } catch (Exception e) { 4163 Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage()); 4164 result = BackupTransport.AGENT_ERROR; 4165 } 4166 return result; 4167 } 4168 4169 @Override 4170 public void execute() { 4171 // Unused in this case 4172 } 4173 4174 @Override 4175 public void operationComplete(int result) { 4176 // got the callback, and our preflightFullBackup() method is waiting for the result 4177 if (MORE_DEBUG) { 4178 Slog.i(TAG, "Preflight op complete, result=" + result); 4179 } 4180 mResult.set(result); 4181 mLatch.countDown(); 4182 } 4183 4184 @Override 4185 public void handleTimeout() { 4186 if (MORE_DEBUG) { 4187 Slog.i(TAG, "Preflight timeout; failing"); 4188 } 4189 mResult.set(BackupTransport.AGENT_ERROR); 4190 mLatch.countDown(); 4191 } 4192 4193 } 4194 4195 class SinglePackageBackupRunner implements Runnable { 4196 final ParcelFileDescriptor mOutput; 4197 final PackageInfo mTarget; 4198 final FullBackupPreflight mPreflight; 4199 final CountDownLatch mLatch; 4200 4201 SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target, 4202 IBackupTransport transport, CountDownLatch latch) throws IOException { 4203 mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor()); 4204 mTarget = target; 4205 mPreflight = new SinglePackageBackupPreflight(transport); 4206 mLatch = latch; 4207 } 4208 4209 @Override 4210 public void run() { 4211 try { 4212 FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor()); 4213 FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName, 4214 mPreflight, false); 4215 engine.backupOnePackage(mTarget); 4216 } catch (Exception e) { 4217 Slog.e(TAG, "Exception during full package backup of " + mTarget); 4218 } finally { 4219 mLatch.countDown(); 4220 try { 4221 mOutput.close(); 4222 } catch (IOException e) { 4223 Slog.w(TAG, "Error closing transport pipe in runner"); 4224 } 4225 } 4226 } 4227 } 4228 } 4229 4230 // ----- Full-data backup scheduling ----- 4231 4232 /** 4233 * Schedule a job to tell us when it's a good time to run a full backup 4234 */ 4235 void scheduleNextFullBackupJob(long transportMinLatency) { 4236 synchronized (mQueueLock) { 4237 if (mFullBackupQueue.size() > 0) { 4238 // schedule the next job at the point in the future when the least-recently 4239 // backed up app comes due for backup again; or immediately if it's already 4240 // due. 4241 final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup; 4242 final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup; 4243 final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL) 4244 ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0; 4245 final long latency = Math.min(transportMinLatency, appLatency); 4246 Runnable r = new Runnable() { 4247 @Override public void run() { 4248 FullBackupJob.schedule(mContext, latency); 4249 } 4250 }; 4251 mBackupHandler.postDelayed(r, 2500); 4252 } else { 4253 if (DEBUG_SCHEDULING) { 4254 Slog.i(TAG, "Full backup queue empty; not scheduling"); 4255 } 4256 } 4257 } 4258 } 4259 4260 /** 4261 * Enqueue full backup for the given app, with a note about when it last ran. 4262 */ 4263 void enqueueFullBackup(String packageName, long lastBackedUp) { 4264 FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp); 4265 synchronized (mQueueLock) { 4266 int N = mFullBackupQueue.size(); 4267 // First, sanity check that we aren't adding a duplicate. Slow but 4268 // straightforward; we'll have at most on the order of a few hundred 4269 // items in this list. 4270 for (int i = N-1; i >= 0; i--) { 4271 final FullBackupEntry e = mFullBackupQueue.get(i); 4272 if (packageName.equals(e.packageName)) { 4273 if (DEBUG) { 4274 Slog.w(TAG, "Removing schedule queue dupe of " + packageName); 4275 } 4276 mFullBackupQueue.remove(i); 4277 } 4278 } 4279 4280 // This is also slow but easy for modest numbers of apps: work backwards 4281 // from the end of the queue until we find an item whose last backup 4282 // time was before this one, then insert this new entry after it. 4283 int which; 4284 for (which = mFullBackupQueue.size() - 1; which >= 0; which--) { 4285 final FullBackupEntry entry = mFullBackupQueue.get(which); 4286 if (entry.lastBackup <= lastBackedUp) { 4287 mFullBackupQueue.add(which + 1, newEntry); 4288 break; 4289 } 4290 } 4291 if (which < 0) { 4292 // this one is earlier than any existing one, so prepend 4293 mFullBackupQueue.add(0, newEntry); 4294 } 4295 } 4296 writeFullBackupScheduleAsync(); 4297 } 4298 4299 /** 4300 * Conditions are right for a full backup operation, so run one. The model we use is 4301 * to perform one app backup per scheduled job execution, and to reschedule the job 4302 * with zero latency as long as conditions remain right and we still have work to do. 4303 * 4304 * @return Whether ongoing work will continue. The return value here will be passed 4305 * along as the return value to the scheduled job's onStartJob() callback. 4306 */ 4307 boolean beginFullBackup(FullBackupJob scheduledJob) { 4308 long now = System.currentTimeMillis(); 4309 FullBackupEntry entry = null; 4310 4311 if (!mEnabled || !mProvisioned) { 4312 // Backups are globally disabled, so don't proceed. We also don't reschedule 4313 // the job driving automatic backups; that job will be scheduled again when 4314 // the user enables backup. 4315 if (MORE_DEBUG) { 4316 Slog.i(TAG, "beginFullBackup but e=" + mEnabled 4317 + " p=" + mProvisioned + "; ignoring"); 4318 } 4319 return false; 4320 } 4321 4322 if (DEBUG_SCHEDULING) { 4323 Slog.i(TAG, "Beginning scheduled full backup operation"); 4324 } 4325 4326 // Great; we're able to run full backup jobs now. See if we have any work to do. 4327 synchronized (mQueueLock) { 4328 if (mRunningFullBackupTask != null) { 4329 Slog.e(TAG, "Backup triggered but one already/still running!"); 4330 return false; 4331 } 4332 4333 if (mFullBackupQueue.size() == 0) { 4334 // no work to do so just bow out 4335 if (DEBUG) { 4336 Slog.i(TAG, "Backup queue empty; doing nothing"); 4337 } 4338 return false; 4339 } 4340 4341 entry = mFullBackupQueue.get(0); 4342 long timeSinceRun = now - entry.lastBackup; 4343 if (timeSinceRun < MIN_FULL_BACKUP_INTERVAL) { 4344 // It's too early to back up the next thing in the queue, so bow out 4345 if (MORE_DEBUG) { 4346 Slog.i(TAG, "Device ready but too early to back up next app"); 4347 } 4348 final long latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun; 4349 mBackupHandler.post(new Runnable() { 4350 @Override public void run() { 4351 FullBackupJob.schedule(mContext, latency); 4352 } 4353 }); 4354 return false; 4355 } 4356 4357 // Okay, the top thing is runnable now. Pop it off and get going. 4358 mFullBackupQueue.remove(0); 4359 CountDownLatch latch = new CountDownLatch(1); 4360 String[] pkg = new String[] {entry.packageName}; 4361 mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true, 4362 scheduledJob, latch); 4363 (new Thread(mRunningFullBackupTask)).start(); 4364 } 4365 4366 return true; 4367 } 4368 4369 // The job scheduler says our constraints don't hold any more, 4370 // so tear down any ongoing backup task right away. 4371 void endFullBackup() { 4372 synchronized (mQueueLock) { 4373 if (mRunningFullBackupTask != null) { 4374 if (DEBUG_SCHEDULING) { 4375 Slog.i(TAG, "Telling running backup to stop"); 4376 } 4377 mRunningFullBackupTask.setRunning(false); 4378 } 4379 } 4380 } 4381 4382 // ----- Restore infrastructure ----- 4383 4384 abstract class RestoreEngine { 4385 static final String TAG = "RestoreEngine"; 4386 4387 public static final int SUCCESS = 0; 4388 public static final int TARGET_FAILURE = -2; 4389 public static final int TRANSPORT_FAILURE = -3; 4390 4391 private AtomicBoolean mRunning = new AtomicBoolean(false); 4392 private AtomicInteger mResult = new AtomicInteger(SUCCESS); 4393 4394 public boolean isRunning() { 4395 return mRunning.get(); 4396 } 4397 4398 public void setRunning(boolean stillRunning) { 4399 synchronized (mRunning) { 4400 mRunning.set(stillRunning); 4401 mRunning.notifyAll(); 4402 } 4403 } 4404 4405 public int waitForResult() { 4406 synchronized (mRunning) { 4407 while (isRunning()) { 4408 try { 4409 mRunning.wait(); 4410 } catch (InterruptedException e) {} 4411 } 4412 } 4413 return getResult(); 4414 } 4415 4416 public int getResult() { 4417 return mResult.get(); 4418 } 4419 4420 public void setResult(int result) { 4421 mResult.set(result); 4422 } 4423 4424 // TODO: abstract restore state and APIs 4425 } 4426 4427 // ----- Full restore from a file/socket ----- 4428 4429 // Description of a file in the restore datastream 4430 static class FileMetadata { 4431 String packageName; // name of the owning app 4432 String installerPackageName; // name of the market-type app that installed the owner 4433 int type; // e.g. BackupAgent.TYPE_DIRECTORY 4434 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN 4435 String path; // subpath within the semantic domain 4436 long mode; // e.g. 0666 (actually int) 4437 long mtime; // last mod time, UTC time_t (actually int) 4438 long size; // bytes of content 4439 4440 @Override 4441 public String toString() { 4442 StringBuilder sb = new StringBuilder(128); 4443 sb.append("FileMetadata{"); 4444 sb.append(packageName); sb.append(','); 4445 sb.append(type); sb.append(','); 4446 sb.append(domain); sb.append(':'); sb.append(path); sb.append(','); 4447 sb.append(size); 4448 sb.append('}'); 4449 return sb.toString(); 4450 } 4451 } 4452 4453 enum RestorePolicy { 4454 IGNORE, 4455 ACCEPT, 4456 ACCEPT_IF_APK 4457 } 4458 4459 // Full restore engine, used by both adb restore and transport-based full restore 4460 class FullRestoreEngine extends RestoreEngine { 4461 // Dedicated observer, if any 4462 IFullBackupRestoreObserver mObserver; 4463 4464 // Where we're delivering the file data as we go 4465 IBackupAgent mAgent; 4466 4467 // Are we permitted to only deliver a specific package's metadata? 4468 PackageInfo mOnlyPackage; 4469 4470 boolean mAllowApks; 4471 boolean mAllowObbs; 4472 4473 // Which package are we currently handling data for? 4474 String mAgentPackage; 4475 4476 // Info for working with the target app process 4477 ApplicationInfo mTargetApp; 4478 4479 // Machinery for restoring OBBs 4480 FullBackupObbConnection mObbConnection = null; 4481 4482 // possible handling states for a given package in the restore dataset 4483 final HashMap<String, RestorePolicy> mPackagePolicies 4484 = new HashMap<String, RestorePolicy>(); 4485 4486 // installer package names for each encountered app, derived from the manifests 4487 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 4488 4489 // Signatures for a given package found in its manifest file 4490 final HashMap<String, Signature[]> mManifestSignatures 4491 = new HashMap<String, Signature[]>(); 4492 4493 // Packages we've already wiped data on when restoring their first file 4494 final HashSet<String> mClearedPackages = new HashSet<String>(); 4495 4496 // How much data have we moved? 4497 long mBytes; 4498 4499 // Working buffer 4500 byte[] mBuffer; 4501 4502 // Pipes for moving data 4503 ParcelFileDescriptor[] mPipes = null; 4504 4505 // Widget blob to be restored out-of-band 4506 byte[] mWidgetData = null; 4507 4508 // Runner that can be placed in a separate thread to do in-process 4509 // invocations of the full restore API asynchronously 4510 class RestoreFileRunnable implements Runnable { 4511 IBackupAgent mAgent; 4512 FileMetadata mInfo; 4513 ParcelFileDescriptor mSocket; 4514 int mToken; 4515 4516 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 4517 ParcelFileDescriptor socket, int token) throws IOException { 4518 mAgent = agent; 4519 mInfo = info; 4520 mToken = token; 4521 4522 // This class is used strictly for process-local binder invocations. The 4523 // semantics of ParcelFileDescriptor differ in this case; in particular, we 4524 // do not automatically get a 'dup'ed descriptor that we can can continue 4525 // to use asynchronously from the caller. So, we make sure to dup it ourselves 4526 // before proceeding to do the restore. 4527 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 4528 } 4529 4530 @Override 4531 public void run() { 4532 try { 4533 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 4534 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 4535 mToken, mBackupManagerBinder); 4536 } catch (RemoteException e) { 4537 // never happens; this is used strictly for local binder calls 4538 } 4539 } 4540 } 4541 4542 public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage, 4543 boolean allowApks, boolean allowObbs) { 4544 mObserver = observer; 4545 mOnlyPackage = onlyPackage; 4546 mAllowApks = allowApks; 4547 mAllowObbs = allowObbs; 4548 mBuffer = new byte[32 * 1024]; 4549 mBytes = 0; 4550 } 4551 4552 public boolean restoreOneFile(InputStream instream) { 4553 if (!isRunning()) { 4554 Slog.w(TAG, "Restore engine used after halting"); 4555 return false; 4556 } 4557 4558 FileMetadata info; 4559 try { 4560 if (MORE_DEBUG) { 4561 Slog.v(TAG, "Reading tar header for restoring file"); 4562 } 4563 info = readTarHeaders(instream); 4564 if (info != null) { 4565 if (MORE_DEBUG) { 4566 dumpFileMetadata(info); 4567 } 4568 4569 final String pkg = info.packageName; 4570 if (!pkg.equals(mAgentPackage)) { 4571 // In the single-package case, it's a semantic error to expect 4572 // one app's data but see a different app's on the wire 4573 if (mOnlyPackage != null) { 4574 if (!pkg.equals(mOnlyPackage.packageName)) { 4575 Slog.w(TAG, "Expected data for " + mOnlyPackage 4576 + " but saw " + pkg); 4577 setResult(RestoreEngine.TRANSPORT_FAILURE); 4578 setRunning(false); 4579 return false; 4580 } 4581 } 4582 4583 // okay, change in package; set up our various 4584 // bookkeeping if we haven't seen it yet 4585 if (!mPackagePolicies.containsKey(pkg)) { 4586 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4587 } 4588 4589 // Clean up the previous agent relationship if necessary, 4590 // and let the observer know we're considering a new app. 4591 if (mAgent != null) { 4592 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 4593 // Now we're really done 4594 tearDownPipes(); 4595 tearDownAgent(mTargetApp); 4596 mTargetApp = null; 4597 mAgentPackage = null; 4598 } 4599 } 4600 4601 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 4602 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 4603 mPackageInstallers.put(pkg, info.installerPackageName); 4604 // We've read only the manifest content itself at this point, 4605 // so consume the footer before looping around to the next 4606 // input file 4607 skipTarPadding(info.size, instream); 4608 sendOnRestorePackage(pkg); 4609 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 4610 // Metadata blobs! 4611 readMetadata(info, instream); 4612 skipTarPadding(info.size, instream); 4613 } else { 4614 // Non-manifest, so it's actual file data. Is this a package 4615 // we're ignoring? 4616 boolean okay = true; 4617 RestorePolicy policy = mPackagePolicies.get(pkg); 4618 switch (policy) { 4619 case IGNORE: 4620 okay = false; 4621 break; 4622 4623 case ACCEPT_IF_APK: 4624 // If we're in accept-if-apk state, then the first file we 4625 // see MUST be the apk. 4626 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 4627 if (DEBUG) Slog.d(TAG, "APK file; installing"); 4628 // Try to install the app. 4629 String installerName = mPackageInstallers.get(pkg); 4630 okay = installApk(info, installerName, instream); 4631 // good to go; promote to ACCEPT 4632 mPackagePolicies.put(pkg, (okay) 4633 ? RestorePolicy.ACCEPT 4634 : RestorePolicy.IGNORE); 4635 // At this point we've consumed this file entry 4636 // ourselves, so just strip the tar footer and 4637 // go on to the next file in the input stream 4638 skipTarPadding(info.size, instream); 4639 return true; 4640 } else { 4641 // File data before (or without) the apk. We can't 4642 // handle it coherently in this case so ignore it. 4643 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4644 okay = false; 4645 } 4646 break; 4647 4648 case ACCEPT: 4649 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 4650 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 4651 // we can take the data without the apk, so we 4652 // *want* to do so. skip the apk by declaring this 4653 // one file not-okay without changing the restore 4654 // policy for the package. 4655 okay = false; 4656 } 4657 break; 4658 4659 default: 4660 // Something has gone dreadfully wrong when determining 4661 // the restore policy from the manifest. Ignore the 4662 // rest of this package's data. 4663 Slog.e(TAG, "Invalid policy from manifest"); 4664 okay = false; 4665 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4666 break; 4667 } 4668 4669 // Is it a *file* we need to drop? 4670 if (!isRestorableFile(info)) { 4671 okay = false; 4672 } 4673 4674 // If the policy is satisfied, go ahead and set up to pipe the 4675 // data to the agent. 4676 if (DEBUG && okay && mAgent != null) { 4677 Slog.i(TAG, "Reusing existing agent instance"); 4678 } 4679 if (okay && mAgent == null) { 4680 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 4681 4682 try { 4683 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 4684 4685 // If we haven't sent any data to this app yet, we probably 4686 // need to clear it first. Check that. 4687 if (!mClearedPackages.contains(pkg)) { 4688 // apps with their own backup agents are 4689 // responsible for coherently managing a full 4690 // restore. 4691 if (mTargetApp.backupAgentName == null) { 4692 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 4693 clearApplicationDataSynchronous(pkg); 4694 } else { 4695 if (DEBUG) Slog.d(TAG, "backup agent (" 4696 + mTargetApp.backupAgentName + ") => no clear"); 4697 } 4698 mClearedPackages.add(pkg); 4699 } else { 4700 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 4701 } 4702 4703 // All set; now set up the IPC and launch the agent 4704 setUpPipes(); 4705 mAgent = bindToAgentSynchronous(mTargetApp, 4706 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 4707 mAgentPackage = pkg; 4708 } catch (IOException e) { 4709 // fall through to error handling 4710 } catch (NameNotFoundException e) { 4711 // fall through to error handling 4712 } 4713 4714 if (mAgent == null) { 4715 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 4716 okay = false; 4717 tearDownPipes(); 4718 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4719 } 4720 } 4721 4722 // Sanity check: make sure we never give data to the wrong app. This 4723 // should never happen but a little paranoia here won't go amiss. 4724 if (okay && !pkg.equals(mAgentPackage)) { 4725 Slog.e(TAG, "Restoring data for " + pkg 4726 + " but agent is for " + mAgentPackage); 4727 okay = false; 4728 } 4729 4730 // At this point we have an agent ready to handle the full 4731 // restore data as well as a pipe for sending data to 4732 // that agent. Tell the agent to start reading from the 4733 // pipe. 4734 if (okay) { 4735 boolean agentSuccess = true; 4736 long toCopy = info.size; 4737 final int token = generateToken(); 4738 try { 4739 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 4740 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 4741 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 4742 + " : " + info.path); 4743 mObbConnection.restoreObbFile(pkg, mPipes[0], 4744 info.size, info.type, info.path, info.mode, 4745 info.mtime, token, mBackupManagerBinder); 4746 } else { 4747 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 4748 + info.path); 4749 // fire up the app's agent listening on the socket. If 4750 // the agent is running in the system process we can't 4751 // just invoke it asynchronously, so we provide a thread 4752 // for it here. 4753 if (mTargetApp.processName.equals("system")) { 4754 Slog.d(TAG, "system process agent - spinning a thread"); 4755 RestoreFileRunnable runner = new RestoreFileRunnable( 4756 mAgent, info, mPipes[0], token); 4757 new Thread(runner, "restore-sys-runner").start(); 4758 } else { 4759 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 4760 info.domain, info.path, info.mode, info.mtime, 4761 token, mBackupManagerBinder); 4762 } 4763 } 4764 } catch (IOException e) { 4765 // couldn't dup the socket for a process-local restore 4766 Slog.d(TAG, "Couldn't establish restore"); 4767 agentSuccess = false; 4768 okay = false; 4769 } catch (RemoteException e) { 4770 // whoops, remote entity went away. We'll eat the content 4771 // ourselves, then, and not copy it over. 4772 Slog.e(TAG, "Agent crashed during full restore"); 4773 agentSuccess = false; 4774 okay = false; 4775 } 4776 4777 // Copy over the data if the agent is still good 4778 if (okay) { 4779 if (MORE_DEBUG) { 4780 Slog.v(TAG, " copying to restore agent: " 4781 + toCopy + " bytes"); 4782 } 4783 boolean pipeOkay = true; 4784 FileOutputStream pipe = new FileOutputStream( 4785 mPipes[1].getFileDescriptor()); 4786 while (toCopy > 0) { 4787 int toRead = (toCopy > mBuffer.length) 4788 ? mBuffer.length : (int)toCopy; 4789 int nRead = instream.read(mBuffer, 0, toRead); 4790 if (nRead >= 0) mBytes += nRead; 4791 if (nRead <= 0) break; 4792 toCopy -= nRead; 4793 4794 // send it to the output pipe as long as things 4795 // are still good 4796 if (pipeOkay) { 4797 try { 4798 pipe.write(mBuffer, 0, nRead); 4799 } catch (IOException e) { 4800 Slog.e(TAG, "Failed to write to restore pipe", e); 4801 pipeOkay = false; 4802 } 4803 } 4804 } 4805 4806 // done sending that file! Now we just need to consume 4807 // the delta from info.size to the end of block. 4808 skipTarPadding(info.size, instream); 4809 4810 // and now that we've sent it all, wait for the remote 4811 // side to acknowledge receipt 4812 agentSuccess = waitUntilOperationComplete(token); 4813 } 4814 4815 // okay, if the remote end failed at any point, deal with 4816 // it by ignoring the rest of the restore on it 4817 if (!agentSuccess) { 4818 if (DEBUG) { 4819 Slog.i(TAG, "Agent failure; ending restore"); 4820 } 4821 mBackupHandler.removeMessages(MSG_TIMEOUT); 4822 tearDownPipes(); 4823 tearDownAgent(mTargetApp); 4824 mAgent = null; 4825 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4826 4827 // If this was a single-package restore, we halt immediately 4828 // with an agent error under these circumstances 4829 if (mOnlyPackage != null) { 4830 setResult(RestoreEngine.TARGET_FAILURE); 4831 setRunning(false); 4832 return false; 4833 } 4834 } 4835 } 4836 4837 // Problems setting up the agent communication, an explicitly 4838 // dropped file, or an already-ignored package: skip to the 4839 // next stream entry by reading and discarding this file. 4840 if (!okay) { 4841 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 4842 long bytesToConsume = (info.size + 511) & ~511; 4843 while (bytesToConsume > 0) { 4844 int toRead = (bytesToConsume > mBuffer.length) 4845 ? mBuffer.length : (int)bytesToConsume; 4846 long nRead = instream.read(mBuffer, 0, toRead); 4847 if (nRead >= 0) mBytes += nRead; 4848 if (nRead <= 0) break; 4849 bytesToConsume -= nRead; 4850 } 4851 } 4852 } 4853 } 4854 } catch (IOException e) { 4855 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 4856 setResult(RestoreEngine.TRANSPORT_FAILURE); 4857 info = null; 4858 } 4859 4860 // If we got here we're either running smoothly or we've finished 4861 if (info == null) { 4862 if (MORE_DEBUG) { 4863 Slog.i(TAG, "No [more] data for this package; tearing down"); 4864 } 4865 tearDownPipes(); 4866 tearDownAgent(mTargetApp); 4867 setRunning(false); 4868 } 4869 return (info != null); 4870 } 4871 4872 void setUpPipes() throws IOException { 4873 mPipes = ParcelFileDescriptor.createPipe(); 4874 } 4875 4876 void tearDownPipes() { 4877 if (mPipes != null) { 4878 try { 4879 mPipes[0].close(); 4880 mPipes[0] = null; 4881 mPipes[1].close(); 4882 mPipes[1] = null; 4883 } catch (IOException e) { 4884 Slog.w(TAG, "Couldn't close agent pipes", e); 4885 } 4886 mPipes = null; 4887 } 4888 } 4889 4890 void tearDownAgent(ApplicationInfo app) { 4891 if (mAgent != null) { 4892 try { 4893 // unbind and tidy up even on timeout or failure, just in case 4894 mActivityManager.unbindBackupAgent(app); 4895 4896 // The agent was running with a stub Application object, so shut it down. 4897 // !!! We hardcode the confirmation UI's package name here rather than use a 4898 // manifest flag! TODO something less direct. 4899 if (app.uid != Process.SYSTEM_UID 4900 && !app.packageName.equals("com.android.backupconfirm")) { 4901 if (DEBUG) Slog.d(TAG, "Killing host process"); 4902 mActivityManager.killApplicationProcess(app.processName, app.uid); 4903 } else { 4904 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 4905 } 4906 } catch (RemoteException e) { 4907 Slog.d(TAG, "Lost app trying to shut down"); 4908 } 4909 mAgent = null; 4910 } 4911 } 4912 4913 class RestoreInstallObserver extends PackageInstallObserver { 4914 final AtomicBoolean mDone = new AtomicBoolean(); 4915 String mPackageName; 4916 int mResult; 4917 4918 public void reset() { 4919 synchronized (mDone) { 4920 mDone.set(false); 4921 } 4922 } 4923 4924 public void waitForCompletion() { 4925 synchronized (mDone) { 4926 while (mDone.get() == false) { 4927 try { 4928 mDone.wait(); 4929 } catch (InterruptedException e) { } 4930 } 4931 } 4932 } 4933 4934 int getResult() { 4935 return mResult; 4936 } 4937 4938 @Override 4939 public void onPackageInstalled(String packageName, int returnCode, 4940 String msg, Bundle extras) { 4941 synchronized (mDone) { 4942 mResult = returnCode; 4943 mPackageName = packageName; 4944 mDone.set(true); 4945 mDone.notifyAll(); 4946 } 4947 } 4948 } 4949 4950 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 4951 final AtomicBoolean mDone = new AtomicBoolean(); 4952 int mResult; 4953 4954 public void reset() { 4955 synchronized (mDone) { 4956 mDone.set(false); 4957 } 4958 } 4959 4960 public void waitForCompletion() { 4961 synchronized (mDone) { 4962 while (mDone.get() == false) { 4963 try { 4964 mDone.wait(); 4965 } catch (InterruptedException e) { } 4966 } 4967 } 4968 } 4969 4970 @Override 4971 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 4972 synchronized (mDone) { 4973 mResult = returnCode; 4974 mDone.set(true); 4975 mDone.notifyAll(); 4976 } 4977 } 4978 } 4979 4980 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 4981 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 4982 4983 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 4984 boolean okay = true; 4985 4986 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 4987 4988 // The file content is an .apk file. Copy it out to a staging location and 4989 // attempt to install it. 4990 File apkFile = new File(mDataDir, info.packageName); 4991 try { 4992 FileOutputStream apkStream = new FileOutputStream(apkFile); 4993 byte[] buffer = new byte[32 * 1024]; 4994 long size = info.size; 4995 while (size > 0) { 4996 long toRead = (buffer.length < size) ? buffer.length : size; 4997 int didRead = instream.read(buffer, 0, (int)toRead); 4998 if (didRead >= 0) mBytes += didRead; 4999 apkStream.write(buffer, 0, didRead); 5000 size -= didRead; 5001 } 5002 apkStream.close(); 5003 5004 // make sure the installer can read it 5005 apkFile.setReadable(true, false); 5006 5007 // Now install it 5008 Uri packageUri = Uri.fromFile(apkFile); 5009 mInstallObserver.reset(); 5010 mPackageManager.installPackage(packageUri, mInstallObserver, 5011 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 5012 installerPackage); 5013 mInstallObserver.waitForCompletion(); 5014 5015 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 5016 // The only time we continue to accept install of data even if the 5017 // apk install failed is if we had already determined that we could 5018 // accept the data regardless. 5019 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 5020 okay = false; 5021 } 5022 } else { 5023 // Okay, the install succeeded. Make sure it was the right app. 5024 boolean uninstall = false; 5025 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 5026 Slog.w(TAG, "Restore stream claimed to include apk for " 5027 + info.packageName + " but apk was really " 5028 + mInstallObserver.mPackageName); 5029 // delete the package we just put in place; it might be fraudulent 5030 okay = false; 5031 uninstall = true; 5032 } else { 5033 try { 5034 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 5035 PackageManager.GET_SIGNATURES); 5036 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 5037 Slog.w(TAG, "Restore stream contains apk of package " 5038 + info.packageName + " but it disallows backup/restore"); 5039 okay = false; 5040 } else { 5041 // So far so good -- do the signatures match the manifest? 5042 Signature[] sigs = mManifestSignatures.get(info.packageName); 5043 if (signaturesMatch(sigs, pkg)) { 5044 // If this is a system-uid app without a declared backup agent, 5045 // don't restore any of the file data. 5046 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 5047 && (pkg.applicationInfo.backupAgentName == null)) { 5048 Slog.w(TAG, "Installed app " + info.packageName 5049 + " has restricted uid and no agent"); 5050 okay = false; 5051 } 5052 } else { 5053 Slog.w(TAG, "Installed app " + info.packageName 5054 + " signatures do not match restore manifest"); 5055 okay = false; 5056 uninstall = true; 5057 } 5058 } 5059 } catch (NameNotFoundException e) { 5060 Slog.w(TAG, "Install of package " + info.packageName 5061 + " succeeded but now not found"); 5062 okay = false; 5063 } 5064 } 5065 5066 // If we're not okay at this point, we need to delete the package 5067 // that we just installed. 5068 if (uninstall) { 5069 mDeleteObserver.reset(); 5070 mPackageManager.deletePackage(mInstallObserver.mPackageName, 5071 mDeleteObserver, 0); 5072 mDeleteObserver.waitForCompletion(); 5073 } 5074 } 5075 } catch (IOException e) { 5076 Slog.e(TAG, "Unable to transcribe restored apk for install"); 5077 okay = false; 5078 } finally { 5079 apkFile.delete(); 5080 } 5081 5082 return okay; 5083 } 5084 5085 // Given an actual file content size, consume the post-content padding mandated 5086 // by the tar format. 5087 void skipTarPadding(long size, InputStream instream) throws IOException { 5088 long partial = (size + 512) % 512; 5089 if (partial > 0) { 5090 final int needed = 512 - (int)partial; 5091 if (MORE_DEBUG) { 5092 Slog.i(TAG, "Skipping tar padding: " + needed + " bytes"); 5093 } 5094 byte[] buffer = new byte[needed]; 5095 if (readExactly(instream, buffer, 0, needed) == needed) { 5096 mBytes += needed; 5097 } else throw new IOException("Unexpected EOF in padding"); 5098 } 5099 } 5100 5101 // Read a widget metadata file, returning the restored blob 5102 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 5103 // Fail on suspiciously large widget dump files 5104 if (info.size > 64 * 1024) { 5105 throw new IOException("Metadata too big; corrupt? size=" + info.size); 5106 } 5107 5108 byte[] buffer = new byte[(int) info.size]; 5109 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5110 mBytes += info.size; 5111 } else throw new IOException("Unexpected EOF in widget data"); 5112 5113 String[] str = new String[1]; 5114 int offset = extractLine(buffer, 0, str); 5115 int version = Integer.parseInt(str[0]); 5116 if (version == BACKUP_MANIFEST_VERSION) { 5117 offset = extractLine(buffer, offset, str); 5118 final String pkg = str[0]; 5119 if (info.packageName.equals(pkg)) { 5120 // Data checks out -- the rest of the buffer is a concatenation of 5121 // binary blobs as described in the comment at writeAppWidgetData() 5122 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 5123 offset, buffer.length - offset); 5124 DataInputStream in = new DataInputStream(bin); 5125 while (bin.available() > 0) { 5126 int token = in.readInt(); 5127 int size = in.readInt(); 5128 if (size > 64 * 1024) { 5129 throw new IOException("Datum " 5130 + Integer.toHexString(token) 5131 + " too big; corrupt? size=" + info.size); 5132 } 5133 switch (token) { 5134 case BACKUP_WIDGET_METADATA_TOKEN: 5135 { 5136 if (MORE_DEBUG) { 5137 Slog.i(TAG, "Got widget metadata for " + info.packageName); 5138 } 5139 mWidgetData = new byte[size]; 5140 in.read(mWidgetData); 5141 break; 5142 } 5143 default: 5144 { 5145 if (DEBUG) { 5146 Slog.i(TAG, "Ignoring metadata blob " 5147 + Integer.toHexString(token) 5148 + " for " + info.packageName); 5149 } 5150 in.skipBytes(size); 5151 break; 5152 } 5153 } 5154 } 5155 } else { 5156 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 5157 + " but widget data for " + pkg); 5158 } 5159 } else { 5160 Slog.w(TAG, "Unsupported metadata version " + version); 5161 } 5162 } 5163 5164 // Returns a policy constant 5165 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 5166 throws IOException { 5167 // Fail on suspiciously large manifest files 5168 if (info.size > 64 * 1024) { 5169 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 5170 } 5171 5172 byte[] buffer = new byte[(int) info.size]; 5173 if (MORE_DEBUG) { 5174 Slog.i(TAG, " readAppManifest() looking for " + info.size + " bytes, " 5175 + mBytes + " already consumed"); 5176 } 5177 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5178 mBytes += info.size; 5179 } else throw new IOException("Unexpected EOF in manifest"); 5180 5181 RestorePolicy policy = RestorePolicy.IGNORE; 5182 String[] str = new String[1]; 5183 int offset = 0; 5184 5185 try { 5186 offset = extractLine(buffer, offset, str); 5187 int version = Integer.parseInt(str[0]); 5188 if (version == BACKUP_MANIFEST_VERSION) { 5189 offset = extractLine(buffer, offset, str); 5190 String manifestPackage = str[0]; 5191 // TODO: handle <original-package> 5192 if (manifestPackage.equals(info.packageName)) { 5193 offset = extractLine(buffer, offset, str); 5194 version = Integer.parseInt(str[0]); // app version 5195 offset = extractLine(buffer, offset, str); 5196 // This is the platform version, which we don't use, but we parse it 5197 // as a safety against corruption in the manifest. 5198 Integer.parseInt(str[0]); 5199 offset = extractLine(buffer, offset, str); 5200 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 5201 offset = extractLine(buffer, offset, str); 5202 boolean hasApk = str[0].equals("1"); 5203 offset = extractLine(buffer, offset, str); 5204 int numSigs = Integer.parseInt(str[0]); 5205 if (numSigs > 0) { 5206 Signature[] sigs = new Signature[numSigs]; 5207 for (int i = 0; i < numSigs; i++) { 5208 offset = extractLine(buffer, offset, str); 5209 sigs[i] = new Signature(str[0]); 5210 } 5211 mManifestSignatures.put(info.packageName, sigs); 5212 5213 // Okay, got the manifest info we need... 5214 try { 5215 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 5216 info.packageName, PackageManager.GET_SIGNATURES); 5217 // Fall through to IGNORE if the app explicitly disallows backup 5218 final int flags = pkgInfo.applicationInfo.flags; 5219 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 5220 // Restore system-uid-space packages only if they have 5221 // defined a custom backup agent 5222 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 5223 || (pkgInfo.applicationInfo.backupAgentName != null)) { 5224 // Verify signatures against any installed version; if they 5225 // don't match, then we fall though and ignore the data. The 5226 // signatureMatch() method explicitly ignores the signature 5227 // check for packages installed on the system partition, because 5228 // such packages are signed with the platform cert instead of 5229 // the app developer's cert, so they're different on every 5230 // device. 5231 if (signaturesMatch(sigs, pkgInfo)) { 5232 if (pkgInfo.versionCode >= version) { 5233 Slog.i(TAG, "Sig + version match; taking data"); 5234 policy = RestorePolicy.ACCEPT; 5235 } else { 5236 // The data is from a newer version of the app than 5237 // is presently installed. That means we can only 5238 // use it if the matching apk is also supplied. 5239 if (mAllowApks) { 5240 Slog.i(TAG, "Data version " + version 5241 + " is newer than installed version " 5242 + pkgInfo.versionCode 5243 + " - requiring apk"); 5244 policy = RestorePolicy.ACCEPT_IF_APK; 5245 } else { 5246 Slog.i(TAG, "Data requires newer version " 5247 + version + "; ignoring"); 5248 policy = RestorePolicy.IGNORE; 5249 } 5250 } 5251 } else { 5252 Slog.w(TAG, "Restore manifest signatures do not match " 5253 + "installed application for " + info.packageName); 5254 } 5255 } else { 5256 Slog.w(TAG, "Package " + info.packageName 5257 + " is system level with no agent"); 5258 } 5259 } else { 5260 if (DEBUG) Slog.i(TAG, "Restore manifest from " 5261 + info.packageName + " but allowBackup=false"); 5262 } 5263 } catch (NameNotFoundException e) { 5264 // Okay, the target app isn't installed. We can process 5265 // the restore properly only if the dataset provides the 5266 // apk file and we can successfully install it. 5267 if (mAllowApks) { 5268 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 5269 + " not installed; requiring apk in dataset"); 5270 policy = RestorePolicy.ACCEPT_IF_APK; 5271 } else { 5272 policy = RestorePolicy.IGNORE; 5273 } 5274 } 5275 5276 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 5277 Slog.i(TAG, "Cannot restore package " + info.packageName 5278 + " without the matching .apk"); 5279 } 5280 } else { 5281 Slog.i(TAG, "Missing signature on backed-up package " 5282 + info.packageName); 5283 } 5284 } else { 5285 Slog.i(TAG, "Expected package " + info.packageName 5286 + " but restore manifest claims " + manifestPackage); 5287 } 5288 } else { 5289 Slog.i(TAG, "Unknown restore manifest version " + version 5290 + " for package " + info.packageName); 5291 } 5292 } catch (NumberFormatException e) { 5293 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 5294 } catch (IllegalArgumentException e) { 5295 Slog.w(TAG, e.getMessage()); 5296 } 5297 5298 return policy; 5299 } 5300 5301 // Builds a line from a byte buffer starting at 'offset', and returns 5302 // the index of the next unconsumed data in the buffer. 5303 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 5304 final int end = buffer.length; 5305 if (offset >= end) throw new IOException("Incomplete data"); 5306 5307 int pos; 5308 for (pos = offset; pos < end; pos++) { 5309 byte c = buffer[pos]; 5310 // at LF we declare end of line, and return the next char as the 5311 // starting point for the next time through 5312 if (c == '\n') { 5313 break; 5314 } 5315 } 5316 outStr[0] = new String(buffer, offset, pos - offset); 5317 pos++; // may be pointing an extra byte past the end but that's okay 5318 return pos; 5319 } 5320 5321 void dumpFileMetadata(FileMetadata info) { 5322 if (DEBUG) { 5323 StringBuilder b = new StringBuilder(128); 5324 5325 // mode string 5326 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 5327 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 5328 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 5329 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 5330 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 5331 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 5332 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 5333 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 5334 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 5335 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 5336 b.append(String.format(" %9d ", info.size)); 5337 5338 Date stamp = new Date(info.mtime); 5339 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 5340 5341 b.append(info.packageName); 5342 b.append(" :: "); 5343 b.append(info.domain); 5344 b.append(" :: "); 5345 b.append(info.path); 5346 5347 Slog.i(TAG, b.toString()); 5348 } 5349 } 5350 5351 // Consume a tar file header block [sequence] and accumulate the relevant metadata 5352 FileMetadata readTarHeaders(InputStream instream) throws IOException { 5353 byte[] block = new byte[512]; 5354 FileMetadata info = null; 5355 5356 boolean gotHeader = readTarHeader(instream, block); 5357 if (gotHeader) { 5358 try { 5359 // okay, presume we're okay, and extract the various metadata 5360 info = new FileMetadata(); 5361 info.size = extractRadix(block, 124, 12, 8); 5362 info.mtime = extractRadix(block, 136, 12, 8); 5363 info.mode = extractRadix(block, 100, 8, 8); 5364 5365 info.path = extractString(block, 345, 155); // prefix 5366 String path = extractString(block, 0, 100); 5367 if (path.length() > 0) { 5368 if (info.path.length() > 0) info.path += '/'; 5369 info.path += path; 5370 } 5371 5372 // tar link indicator field: 1 byte at offset 156 in the header. 5373 int typeChar = block[156]; 5374 if (typeChar == 'x') { 5375 // pax extended header, so we need to read that 5376 gotHeader = readPaxExtendedHeader(instream, info); 5377 if (gotHeader) { 5378 // and after a pax extended header comes another real header -- read 5379 // that to find the real file type 5380 gotHeader = readTarHeader(instream, block); 5381 } 5382 if (!gotHeader) throw new IOException("Bad or missing pax header"); 5383 5384 typeChar = block[156]; 5385 } 5386 5387 switch (typeChar) { 5388 case '0': info.type = BackupAgent.TYPE_FILE; break; 5389 case '5': { 5390 info.type = BackupAgent.TYPE_DIRECTORY; 5391 if (info.size != 0) { 5392 Slog.w(TAG, "Directory entry with nonzero size in header"); 5393 info.size = 0; 5394 } 5395 break; 5396 } 5397 case 0: { 5398 // presume EOF 5399 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 5400 return null; 5401 } 5402 default: { 5403 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 5404 throw new IOException("Unknown entity type " + typeChar); 5405 } 5406 } 5407 5408 // Parse out the path 5409 // 5410 // first: apps/shared/unrecognized 5411 if (FullBackup.SHARED_PREFIX.regionMatches(0, 5412 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 5413 // File in shared storage. !!! TODO: implement this. 5414 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 5415 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 5416 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 5417 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 5418 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 5419 info.path, 0, FullBackup.APPS_PREFIX.length())) { 5420 // App content! Parse out the package name and domain 5421 5422 // strip the apps/ prefix 5423 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 5424 5425 // extract the package name 5426 int slash = info.path.indexOf('/'); 5427 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 5428 info.packageName = info.path.substring(0, slash); 5429 info.path = info.path.substring(slash+1); 5430 5431 // if it's a manifest or metadata payload we're done, otherwise parse 5432 // out the domain into which the file will be restored 5433 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 5434 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 5435 slash = info.path.indexOf('/'); 5436 if (slash < 0) { 5437 throw new IOException("Illegal semantic path in non-manifest " 5438 + info.path); 5439 } 5440 info.domain = info.path.substring(0, slash); 5441 info.path = info.path.substring(slash + 1); 5442 } 5443 } 5444 } catch (IOException e) { 5445 if (DEBUG) { 5446 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 5447 HEXLOG(block); 5448 } 5449 throw e; 5450 } 5451 } 5452 return info; 5453 } 5454 5455 private boolean isRestorableFile(FileMetadata info) { 5456 if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { 5457 if (MORE_DEBUG) { 5458 Slog.i(TAG, "Dropping cache file path " + info.path); 5459 } 5460 return false; 5461 } 5462 5463 if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) { 5464 // It's possible this is "no-backup" dir contents in an archive stream 5465 // produced on a device running a version of the OS that predates that 5466 // API. Respect the no-backup intention and don't let the data get to 5467 // the app. 5468 if (info.path.startsWith("no_backup/")) { 5469 if (MORE_DEBUG) { 5470 Slog.i(TAG, "Dropping no_backup file path " + info.path); 5471 } 5472 return false; 5473 } 5474 } 5475 5476 // The path needs to be canonical 5477 if (info.path.contains("..") || info.path.contains("//")) { 5478 if (MORE_DEBUG) { 5479 Slog.w(TAG, "Dropping invalid path " + info.path); 5480 } 5481 return false; 5482 } 5483 5484 // Otherwise we think this file is good to go 5485 return true; 5486 } 5487 5488 private void HEXLOG(byte[] block) { 5489 int offset = 0; 5490 int todo = block.length; 5491 StringBuilder buf = new StringBuilder(64); 5492 while (todo > 0) { 5493 buf.append(String.format("%04x ", offset)); 5494 int numThisLine = (todo > 16) ? 16 : todo; 5495 for (int i = 0; i < numThisLine; i++) { 5496 buf.append(String.format("%02x ", block[offset+i])); 5497 } 5498 Slog.i("hexdump", buf.toString()); 5499 buf.setLength(0); 5500 todo -= numThisLine; 5501 offset += numThisLine; 5502 } 5503 } 5504 5505 // Read exactly the given number of bytes into a buffer at the stated offset. 5506 // Returns false if EOF is encountered before the requested number of bytes 5507 // could be read. 5508 int readExactly(InputStream in, byte[] buffer, int offset, int size) 5509 throws IOException { 5510 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 5511if (MORE_DEBUG) Slog.i(TAG, " ... readExactly(" + size + ") called"); 5512 int soFar = 0; 5513 while (soFar < size) { 5514 int nRead = in.read(buffer, offset + soFar, size - soFar); 5515 if (nRead <= 0) { 5516 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 5517 break; 5518 } 5519 soFar += nRead; 5520if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar)); 5521 } 5522 return soFar; 5523 } 5524 5525 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 5526 final int got = readExactly(instream, block, 0, 512); 5527 if (got == 0) return false; // Clean EOF 5528 if (got < 512) throw new IOException("Unable to read full block header"); 5529 mBytes += 512; 5530 return true; 5531 } 5532 5533 // overwrites 'info' fields based on the pax extended header 5534 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 5535 throws IOException { 5536 // We should never see a pax extended header larger than this 5537 if (info.size > 32*1024) { 5538 Slog.w(TAG, "Suspiciously large pax header size " + info.size 5539 + " - aborting"); 5540 throw new IOException("Sanity failure: pax header size " + info.size); 5541 } 5542 5543 // read whole blocks, not just the content size 5544 int numBlocks = (int)((info.size + 511) >> 9); 5545 byte[] data = new byte[numBlocks * 512]; 5546 if (readExactly(instream, data, 0, data.length) < data.length) { 5547 throw new IOException("Unable to read full pax header"); 5548 } 5549 mBytes += data.length; 5550 5551 final int contentSize = (int) info.size; 5552 int offset = 0; 5553 do { 5554 // extract the line at 'offset' 5555 int eol = offset+1; 5556 while (eol < contentSize && data[eol] != ' ') eol++; 5557 if (eol >= contentSize) { 5558 // error: we just hit EOD looking for the end of the size field 5559 throw new IOException("Invalid pax data"); 5560 } 5561 // eol points to the space between the count and the key 5562 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 5563 int key = eol + 1; // start of key=value 5564 eol = offset + linelen - 1; // trailing LF 5565 int value; 5566 for (value = key+1; data[value] != '=' && value <= eol; value++); 5567 if (value > eol) { 5568 throw new IOException("Invalid pax declaration"); 5569 } 5570 5571 // pax requires that key/value strings be in UTF-8 5572 String keyStr = new String(data, key, value-key, "UTF-8"); 5573 // -1 to strip the trailing LF 5574 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 5575 5576 if ("path".equals(keyStr)) { 5577 info.path = valStr; 5578 } else if ("size".equals(keyStr)) { 5579 info.size = Long.parseLong(valStr); 5580 } else { 5581 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 5582 } 5583 5584 offset += linelen; 5585 } while (offset < contentSize); 5586 5587 return true; 5588 } 5589 5590 long extractRadix(byte[] data, int offset, int maxChars, int radix) 5591 throws IOException { 5592 long value = 0; 5593 final int end = offset + maxChars; 5594 for (int i = offset; i < end; i++) { 5595 final byte b = data[i]; 5596 // Numeric fields in tar can terminate with either NUL or SPC 5597 if (b == 0 || b == ' ') break; 5598 if (b < '0' || b > ('0' + radix - 1)) { 5599 throw new IOException("Invalid number in header: '" + (char)b 5600 + "' for radix " + radix); 5601 } 5602 value = radix * value + (b - '0'); 5603 } 5604 return value; 5605 } 5606 5607 String extractString(byte[] data, int offset, int maxChars) throws IOException { 5608 final int end = offset + maxChars; 5609 int eos = offset; 5610 // tar string fields terminate early with a NUL 5611 while (eos < end && data[eos] != 0) eos++; 5612 return new String(data, offset, eos-offset, "US-ASCII"); 5613 } 5614 5615 void sendStartRestore() { 5616 if (mObserver != null) { 5617 try { 5618 mObserver.onStartRestore(); 5619 } catch (RemoteException e) { 5620 Slog.w(TAG, "full restore observer went away: startRestore"); 5621 mObserver = null; 5622 } 5623 } 5624 } 5625 5626 void sendOnRestorePackage(String name) { 5627 if (mObserver != null) { 5628 try { 5629 // TODO: use a more user-friendly name string 5630 mObserver.onRestorePackage(name); 5631 } catch (RemoteException e) { 5632 Slog.w(TAG, "full restore observer went away: restorePackage"); 5633 mObserver = null; 5634 } 5635 } 5636 } 5637 5638 void sendEndRestore() { 5639 if (mObserver != null) { 5640 try { 5641 mObserver.onEndRestore(); 5642 } catch (RemoteException e) { 5643 Slog.w(TAG, "full restore observer went away: endRestore"); 5644 mObserver = null; 5645 } 5646 } 5647 } 5648 } 5649 5650 // ***** end new engine class *** 5651 5652 class PerformAdbRestoreTask implements Runnable { 5653 ParcelFileDescriptor mInputFile; 5654 String mCurrentPassword; 5655 String mDecryptPassword; 5656 IFullBackupRestoreObserver mObserver; 5657 AtomicBoolean mLatchObject; 5658 IBackupAgent mAgent; 5659 String mAgentPackage; 5660 ApplicationInfo mTargetApp; 5661 FullBackupObbConnection mObbConnection = null; 5662 ParcelFileDescriptor[] mPipes = null; 5663 byte[] mWidgetData = null; 5664 5665 long mBytes; 5666 5667 // possible handling states for a given package in the restore dataset 5668 final HashMap<String, RestorePolicy> mPackagePolicies 5669 = new HashMap<String, RestorePolicy>(); 5670 5671 // installer package names for each encountered app, derived from the manifests 5672 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 5673 5674 // Signatures for a given package found in its manifest file 5675 final HashMap<String, Signature[]> mManifestSignatures 5676 = new HashMap<String, Signature[]>(); 5677 5678 // Packages we've already wiped data on when restoring their first file 5679 final HashSet<String> mClearedPackages = new HashSet<String>(); 5680 5681 PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword, 5682 IFullBackupRestoreObserver observer, AtomicBoolean latch) { 5683 mInputFile = fd; 5684 mCurrentPassword = curPassword; 5685 mDecryptPassword = decryptPassword; 5686 mObserver = observer; 5687 mLatchObject = latch; 5688 mAgent = null; 5689 mAgentPackage = null; 5690 mTargetApp = null; 5691 mObbConnection = new FullBackupObbConnection(); 5692 5693 // Which packages we've already wiped data on. We prepopulate this 5694 // with a whitelist of packages known to be unclearable. 5695 mClearedPackages.add("android"); 5696 mClearedPackages.add(SETTINGS_PACKAGE); 5697 } 5698 5699 class RestoreFileRunnable implements Runnable { 5700 IBackupAgent mAgent; 5701 FileMetadata mInfo; 5702 ParcelFileDescriptor mSocket; 5703 int mToken; 5704 5705 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 5706 ParcelFileDescriptor socket, int token) throws IOException { 5707 mAgent = agent; 5708 mInfo = info; 5709 mToken = token; 5710 5711 // This class is used strictly for process-local binder invocations. The 5712 // semantics of ParcelFileDescriptor differ in this case; in particular, we 5713 // do not automatically get a 'dup'ed descriptor that we can can continue 5714 // to use asynchronously from the caller. So, we make sure to dup it ourselves 5715 // before proceeding to do the restore. 5716 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 5717 } 5718 5719 @Override 5720 public void run() { 5721 try { 5722 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 5723 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 5724 mToken, mBackupManagerBinder); 5725 } catch (RemoteException e) { 5726 // never happens; this is used strictly for local binder calls 5727 } 5728 } 5729 } 5730 5731 @Override 5732 public void run() { 5733 Slog.i(TAG, "--- Performing full-dataset restore ---"); 5734 mObbConnection.establish(); 5735 sendStartRestore(); 5736 5737 // Are we able to restore shared-storage data? 5738 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 5739 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT); 5740 } 5741 5742 FileInputStream rawInStream = null; 5743 DataInputStream rawDataIn = null; 5744 try { 5745 if (!backupPasswordMatches(mCurrentPassword)) { 5746 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 5747 return; 5748 } 5749 5750 mBytes = 0; 5751 byte[] buffer = new byte[32 * 1024]; 5752 rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); 5753 rawDataIn = new DataInputStream(rawInStream); 5754 5755 // First, parse out the unencrypted/uncompressed header 5756 boolean compressed = false; 5757 InputStream preCompressStream = rawInStream; 5758 final InputStream in; 5759 5760 boolean okay = false; 5761 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length(); 5762 byte[] streamHeader = new byte[headerLen]; 5763 rawDataIn.readFully(streamHeader); 5764 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8"); 5765 if (Arrays.equals(magicBytes, streamHeader)) { 5766 // okay, header looks good. now parse out the rest of the fields. 5767 String s = readHeaderLine(rawInStream); 5768 final int archiveVersion = Integer.parseInt(s); 5769 if (archiveVersion <= BACKUP_FILE_VERSION) { 5770 // okay, it's a version we recognize. if it's version 1, we may need 5771 // to try two different PBKDF2 regimes to compare checksums. 5772 final boolean pbkdf2Fallback = (archiveVersion == 1); 5773 5774 s = readHeaderLine(rawInStream); 5775 compressed = (Integer.parseInt(s) != 0); 5776 s = readHeaderLine(rawInStream); 5777 if (s.equals("none")) { 5778 // no more header to parse; we're good to go 5779 okay = true; 5780 } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) { 5781 preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback, 5782 rawInStream); 5783 if (preCompressStream != null) { 5784 okay = true; 5785 } 5786 } else Slog.w(TAG, "Archive is encrypted but no password given"); 5787 } else Slog.w(TAG, "Wrong header version: " + s); 5788 } else Slog.w(TAG, "Didn't read the right header magic"); 5789 5790 if (!okay) { 5791 Slog.w(TAG, "Invalid restore data; aborting."); 5792 return; 5793 } 5794 5795 // okay, use the right stream layer based on compression 5796 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream; 5797 5798 boolean didRestore; 5799 do { 5800 didRestore = restoreOneFile(in, buffer); 5801 } while (didRestore); 5802 5803 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); 5804 } catch (IOException e) { 5805 Slog.e(TAG, "Unable to read restore input"); 5806 } finally { 5807 tearDownPipes(); 5808 tearDownAgent(mTargetApp); 5809 5810 try { 5811 if (rawDataIn != null) rawDataIn.close(); 5812 if (rawInStream != null) rawInStream.close(); 5813 mInputFile.close(); 5814 } catch (IOException e) { 5815 Slog.w(TAG, "Close of restore data pipe threw", e); 5816 /* nothing we can do about this */ 5817 } 5818 synchronized (mCurrentOpLock) { 5819 mCurrentOperations.clear(); 5820 } 5821 synchronized (mLatchObject) { 5822 mLatchObject.set(true); 5823 mLatchObject.notifyAll(); 5824 } 5825 mObbConnection.tearDown(); 5826 sendEndRestore(); 5827 Slog.d(TAG, "Full restore pass complete."); 5828 mWakelock.release(); 5829 } 5830 } 5831 5832 String readHeaderLine(InputStream in) throws IOException { 5833 int c; 5834 StringBuilder buffer = new StringBuilder(80); 5835 while ((c = in.read()) >= 0) { 5836 if (c == '\n') break; // consume and discard the newlines 5837 buffer.append((char)c); 5838 } 5839 return buffer.toString(); 5840 } 5841 5842 InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt, 5843 int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream, 5844 boolean doLog) { 5845 InputStream result = null; 5846 5847 try { 5848 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 5849 SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt, 5850 rounds); 5851 byte[] IV = hexToByteArray(userIvHex); 5852 IvParameterSpec ivSpec = new IvParameterSpec(IV); 5853 c.init(Cipher.DECRYPT_MODE, 5854 new SecretKeySpec(userKey.getEncoded(), "AES"), 5855 ivSpec); 5856 byte[] mkCipher = hexToByteArray(masterKeyBlobHex); 5857 byte[] mkBlob = c.doFinal(mkCipher); 5858 5859 // first, the master key IV 5860 int offset = 0; 5861 int len = mkBlob[offset++]; 5862 IV = Arrays.copyOfRange(mkBlob, offset, offset + len); 5863 offset += len; 5864 // then the master key itself 5865 len = mkBlob[offset++]; 5866 byte[] mk = Arrays.copyOfRange(mkBlob, 5867 offset, offset + len); 5868 offset += len; 5869 // and finally the master key checksum hash 5870 len = mkBlob[offset++]; 5871 byte[] mkChecksum = Arrays.copyOfRange(mkBlob, 5872 offset, offset + len); 5873 5874 // now validate the decrypted master key against the checksum 5875 byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds); 5876 if (Arrays.equals(calculatedCk, mkChecksum)) { 5877 ivSpec = new IvParameterSpec(IV); 5878 c.init(Cipher.DECRYPT_MODE, 5879 new SecretKeySpec(mk, "AES"), 5880 ivSpec); 5881 // Only if all of the above worked properly will 'result' be assigned 5882 result = new CipherInputStream(rawInStream, c); 5883 } else if (doLog) Slog.w(TAG, "Incorrect password"); 5884 } catch (InvalidAlgorithmParameterException e) { 5885 if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e); 5886 } catch (BadPaddingException e) { 5887 // This case frequently occurs when the wrong password is used to decrypt 5888 // the master key. Use the identical "incorrect password" log text as is 5889 // used in the checksum failure log in order to avoid providing additional 5890 // information to an attacker. 5891 if (doLog) Slog.w(TAG, "Incorrect password"); 5892 } catch (IllegalBlockSizeException e) { 5893 if (doLog) Slog.w(TAG, "Invalid block size in master key"); 5894 } catch (NoSuchAlgorithmException e) { 5895 if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!"); 5896 } catch (NoSuchPaddingException e) { 5897 if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!"); 5898 } catch (InvalidKeyException e) { 5899 if (doLog) Slog.w(TAG, "Illegal password; aborting"); 5900 } 5901 5902 return result; 5903 } 5904 5905 InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback, 5906 InputStream rawInStream) { 5907 InputStream result = null; 5908 try { 5909 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) { 5910 5911 String userSaltHex = readHeaderLine(rawInStream); // 5 5912 byte[] userSalt = hexToByteArray(userSaltHex); 5913 5914 String ckSaltHex = readHeaderLine(rawInStream); // 6 5915 byte[] ckSalt = hexToByteArray(ckSaltHex); 5916 5917 int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 5918 String userIvHex = readHeaderLine(rawInStream); // 8 5919 5920 String masterKeyBlobHex = readHeaderLine(rawInStream); // 9 5921 5922 // decrypt the master key blob 5923 result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt, 5924 rounds, userIvHex, masterKeyBlobHex, rawInStream, false); 5925 if (result == null && pbkdf2Fallback) { 5926 result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt, 5927 rounds, userIvHex, masterKeyBlobHex, rawInStream, true); 5928 } 5929 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName); 5930 } catch (NumberFormatException e) { 5931 Slog.w(TAG, "Can't parse restore data header"); 5932 } catch (IOException e) { 5933 Slog.w(TAG, "Can't read input header"); 5934 } 5935 5936 return result; 5937 } 5938 5939 boolean restoreOneFile(InputStream instream, byte[] buffer) { 5940 FileMetadata info; 5941 try { 5942 info = readTarHeaders(instream); 5943 if (info != null) { 5944 if (MORE_DEBUG) { 5945 dumpFileMetadata(info); 5946 } 5947 5948 final String pkg = info.packageName; 5949 if (!pkg.equals(mAgentPackage)) { 5950 // okay, change in package; set up our various 5951 // bookkeeping if we haven't seen it yet 5952 if (!mPackagePolicies.containsKey(pkg)) { 5953 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5954 } 5955 5956 // Clean up the previous agent relationship if necessary, 5957 // and let the observer know we're considering a new app. 5958 if (mAgent != null) { 5959 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 5960 // Now we're really done 5961 tearDownPipes(); 5962 tearDownAgent(mTargetApp); 5963 mTargetApp = null; 5964 mAgentPackage = null; 5965 } 5966 } 5967 5968 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 5969 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 5970 mPackageInstallers.put(pkg, info.installerPackageName); 5971 // We've read only the manifest content itself at this point, 5972 // so consume the footer before looping around to the next 5973 // input file 5974 skipTarPadding(info.size, instream); 5975 sendOnRestorePackage(pkg); 5976 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 5977 // Metadata blobs! 5978 readMetadata(info, instream); 5979 skipTarPadding(info.size, instream); 5980 } else { 5981 // Non-manifest, so it's actual file data. Is this a package 5982 // we're ignoring? 5983 boolean okay = true; 5984 RestorePolicy policy = mPackagePolicies.get(pkg); 5985 switch (policy) { 5986 case IGNORE: 5987 okay = false; 5988 break; 5989 5990 case ACCEPT_IF_APK: 5991 // If we're in accept-if-apk state, then the first file we 5992 // see MUST be the apk. 5993 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5994 if (DEBUG) Slog.d(TAG, "APK file; installing"); 5995 // Try to install the app. 5996 String installerName = mPackageInstallers.get(pkg); 5997 okay = installApk(info, installerName, instream); 5998 // good to go; promote to ACCEPT 5999 mPackagePolicies.put(pkg, (okay) 6000 ? RestorePolicy.ACCEPT 6001 : RestorePolicy.IGNORE); 6002 // At this point we've consumed this file entry 6003 // ourselves, so just strip the tar footer and 6004 // go on to the next file in the input stream 6005 skipTarPadding(info.size, instream); 6006 return true; 6007 } else { 6008 // File data before (or without) the apk. We can't 6009 // handle it coherently in this case so ignore it. 6010 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6011 okay = false; 6012 } 6013 break; 6014 6015 case ACCEPT: 6016 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 6017 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 6018 // we can take the data without the apk, so we 6019 // *want* to do so. skip the apk by declaring this 6020 // one file not-okay without changing the restore 6021 // policy for the package. 6022 okay = false; 6023 } 6024 break; 6025 6026 default: 6027 // Something has gone dreadfully wrong when determining 6028 // the restore policy from the manifest. Ignore the 6029 // rest of this package's data. 6030 Slog.e(TAG, "Invalid policy from manifest"); 6031 okay = false; 6032 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6033 break; 6034 } 6035 6036 // The path needs to be canonical 6037 if (info.path.contains("..") || info.path.contains("//")) { 6038 if (MORE_DEBUG) { 6039 Slog.w(TAG, "Dropping invalid path " + info.path); 6040 } 6041 okay = false; 6042 } 6043 6044 // If the policy is satisfied, go ahead and set up to pipe the 6045 // data to the agent. 6046 if (DEBUG && okay && mAgent != null) { 6047 Slog.i(TAG, "Reusing existing agent instance"); 6048 } 6049 if (okay && mAgent == null) { 6050 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 6051 6052 try { 6053 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 6054 6055 // If we haven't sent any data to this app yet, we probably 6056 // need to clear it first. Check that. 6057 if (!mClearedPackages.contains(pkg)) { 6058 // apps with their own backup agents are 6059 // responsible for coherently managing a full 6060 // restore. 6061 if (mTargetApp.backupAgentName == null) { 6062 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 6063 clearApplicationDataSynchronous(pkg); 6064 } else { 6065 if (DEBUG) Slog.d(TAG, "backup agent (" 6066 + mTargetApp.backupAgentName + ") => no clear"); 6067 } 6068 mClearedPackages.add(pkg); 6069 } else { 6070 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 6071 } 6072 6073 // All set; now set up the IPC and launch the agent 6074 setUpPipes(); 6075 mAgent = bindToAgentSynchronous(mTargetApp, 6076 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 6077 mAgentPackage = pkg; 6078 } catch (IOException e) { 6079 // fall through to error handling 6080 } catch (NameNotFoundException e) { 6081 // fall through to error handling 6082 } 6083 6084 if (mAgent == null) { 6085 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 6086 okay = false; 6087 tearDownPipes(); 6088 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6089 } 6090 } 6091 6092 // Sanity check: make sure we never give data to the wrong app. This 6093 // should never happen but a little paranoia here won't go amiss. 6094 if (okay && !pkg.equals(mAgentPackage)) { 6095 Slog.e(TAG, "Restoring data for " + pkg 6096 + " but agent is for " + mAgentPackage); 6097 okay = false; 6098 } 6099 6100 // At this point we have an agent ready to handle the full 6101 // restore data as well as a pipe for sending data to 6102 // that agent. Tell the agent to start reading from the 6103 // pipe. 6104 if (okay) { 6105 boolean agentSuccess = true; 6106 long toCopy = info.size; 6107 final int token = generateToken(); 6108 try { 6109 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 6110 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 6111 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 6112 + " : " + info.path); 6113 mObbConnection.restoreObbFile(pkg, mPipes[0], 6114 info.size, info.type, info.path, info.mode, 6115 info.mtime, token, mBackupManagerBinder); 6116 } else { 6117 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 6118 + info.path); 6119 // fire up the app's agent listening on the socket. If 6120 // the agent is running in the system process we can't 6121 // just invoke it asynchronously, so we provide a thread 6122 // for it here. 6123 if (mTargetApp.processName.equals("system")) { 6124 Slog.d(TAG, "system process agent - spinning a thread"); 6125 RestoreFileRunnable runner = new RestoreFileRunnable( 6126 mAgent, info, mPipes[0], token); 6127 new Thread(runner, "restore-sys-runner").start(); 6128 } else { 6129 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 6130 info.domain, info.path, info.mode, info.mtime, 6131 token, mBackupManagerBinder); 6132 } 6133 } 6134 } catch (IOException e) { 6135 // couldn't dup the socket for a process-local restore 6136 Slog.d(TAG, "Couldn't establish restore"); 6137 agentSuccess = false; 6138 okay = false; 6139 } catch (RemoteException e) { 6140 // whoops, remote entity went away. We'll eat the content 6141 // ourselves, then, and not copy it over. 6142 Slog.e(TAG, "Agent crashed during full restore"); 6143 agentSuccess = false; 6144 okay = false; 6145 } 6146 6147 // Copy over the data if the agent is still good 6148 if (okay) { 6149 boolean pipeOkay = true; 6150 FileOutputStream pipe = new FileOutputStream( 6151 mPipes[1].getFileDescriptor()); 6152 while (toCopy > 0) { 6153 int toRead = (toCopy > buffer.length) 6154 ? buffer.length : (int)toCopy; 6155 int nRead = instream.read(buffer, 0, toRead); 6156 if (nRead >= 0) mBytes += nRead; 6157 if (nRead <= 0) break; 6158 toCopy -= nRead; 6159 6160 // send it to the output pipe as long as things 6161 // are still good 6162 if (pipeOkay) { 6163 try { 6164 pipe.write(buffer, 0, nRead); 6165 } catch (IOException e) { 6166 Slog.e(TAG, "Failed to write to restore pipe", e); 6167 pipeOkay = false; 6168 } 6169 } 6170 } 6171 6172 // done sending that file! Now we just need to consume 6173 // the delta from info.size to the end of block. 6174 skipTarPadding(info.size, instream); 6175 6176 // and now that we've sent it all, wait for the remote 6177 // side to acknowledge receipt 6178 agentSuccess = waitUntilOperationComplete(token); 6179 } 6180 6181 // okay, if the remote end failed at any point, deal with 6182 // it by ignoring the rest of the restore on it 6183 if (!agentSuccess) { 6184 mBackupHandler.removeMessages(MSG_TIMEOUT); 6185 tearDownPipes(); 6186 tearDownAgent(mTargetApp); 6187 mAgent = null; 6188 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6189 } 6190 } 6191 6192 // Problems setting up the agent communication, or an already- 6193 // ignored package: skip to the next tar stream entry by 6194 // reading and discarding this file. 6195 if (!okay) { 6196 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 6197 long bytesToConsume = (info.size + 511) & ~511; 6198 while (bytesToConsume > 0) { 6199 int toRead = (bytesToConsume > buffer.length) 6200 ? buffer.length : (int)bytesToConsume; 6201 long nRead = instream.read(buffer, 0, toRead); 6202 if (nRead >= 0) mBytes += nRead; 6203 if (nRead <= 0) break; 6204 bytesToConsume -= nRead; 6205 } 6206 } 6207 } 6208 } 6209 } catch (IOException e) { 6210 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 6211 // treat as EOF 6212 info = null; 6213 } 6214 6215 return (info != null); 6216 } 6217 6218 void setUpPipes() throws IOException { 6219 mPipes = ParcelFileDescriptor.createPipe(); 6220 } 6221 6222 void tearDownPipes() { 6223 if (mPipes != null) { 6224 try { 6225 mPipes[0].close(); 6226 mPipes[0] = null; 6227 mPipes[1].close(); 6228 mPipes[1] = null; 6229 } catch (IOException e) { 6230 Slog.w(TAG, "Couldn't close agent pipes", e); 6231 } 6232 mPipes = null; 6233 } 6234 } 6235 6236 void tearDownAgent(ApplicationInfo app) { 6237 if (mAgent != null) { 6238 try { 6239 // unbind and tidy up even on timeout or failure, just in case 6240 mActivityManager.unbindBackupAgent(app); 6241 6242 // The agent was running with a stub Application object, so shut it down. 6243 // !!! We hardcode the confirmation UI's package name here rather than use a 6244 // manifest flag! TODO something less direct. 6245 if (app.uid != Process.SYSTEM_UID 6246 && !app.packageName.equals("com.android.backupconfirm")) { 6247 if (DEBUG) Slog.d(TAG, "Killing host process"); 6248 mActivityManager.killApplicationProcess(app.processName, app.uid); 6249 } else { 6250 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 6251 } 6252 } catch (RemoteException e) { 6253 Slog.d(TAG, "Lost app trying to shut down"); 6254 } 6255 mAgent = null; 6256 } 6257 } 6258 6259 class RestoreInstallObserver extends PackageInstallObserver { 6260 final AtomicBoolean mDone = new AtomicBoolean(); 6261 String mPackageName; 6262 int mResult; 6263 6264 public void reset() { 6265 synchronized (mDone) { 6266 mDone.set(false); 6267 } 6268 } 6269 6270 public void waitForCompletion() { 6271 synchronized (mDone) { 6272 while (mDone.get() == false) { 6273 try { 6274 mDone.wait(); 6275 } catch (InterruptedException e) { } 6276 } 6277 } 6278 } 6279 6280 int getResult() { 6281 return mResult; 6282 } 6283 6284 @Override 6285 public void onPackageInstalled(String packageName, int returnCode, 6286 String msg, Bundle extras) { 6287 synchronized (mDone) { 6288 mResult = returnCode; 6289 mPackageName = packageName; 6290 mDone.set(true); 6291 mDone.notifyAll(); 6292 } 6293 } 6294 } 6295 6296 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 6297 final AtomicBoolean mDone = new AtomicBoolean(); 6298 int mResult; 6299 6300 public void reset() { 6301 synchronized (mDone) { 6302 mDone.set(false); 6303 } 6304 } 6305 6306 public void waitForCompletion() { 6307 synchronized (mDone) { 6308 while (mDone.get() == false) { 6309 try { 6310 mDone.wait(); 6311 } catch (InterruptedException e) { } 6312 } 6313 } 6314 } 6315 6316 @Override 6317 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 6318 synchronized (mDone) { 6319 mResult = returnCode; 6320 mDone.set(true); 6321 mDone.notifyAll(); 6322 } 6323 } 6324 } 6325 6326 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 6327 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 6328 6329 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 6330 boolean okay = true; 6331 6332 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 6333 6334 // The file content is an .apk file. Copy it out to a staging location and 6335 // attempt to install it. 6336 File apkFile = new File(mDataDir, info.packageName); 6337 try { 6338 FileOutputStream apkStream = new FileOutputStream(apkFile); 6339 byte[] buffer = new byte[32 * 1024]; 6340 long size = info.size; 6341 while (size > 0) { 6342 long toRead = (buffer.length < size) ? buffer.length : size; 6343 int didRead = instream.read(buffer, 0, (int)toRead); 6344 if (didRead >= 0) mBytes += didRead; 6345 apkStream.write(buffer, 0, didRead); 6346 size -= didRead; 6347 } 6348 apkStream.close(); 6349 6350 // make sure the installer can read it 6351 apkFile.setReadable(true, false); 6352 6353 // Now install it 6354 Uri packageUri = Uri.fromFile(apkFile); 6355 mInstallObserver.reset(); 6356 mPackageManager.installPackage(packageUri, mInstallObserver, 6357 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 6358 installerPackage); 6359 mInstallObserver.waitForCompletion(); 6360 6361 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 6362 // The only time we continue to accept install of data even if the 6363 // apk install failed is if we had already determined that we could 6364 // accept the data regardless. 6365 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 6366 okay = false; 6367 } 6368 } else { 6369 // Okay, the install succeeded. Make sure it was the right app. 6370 boolean uninstall = false; 6371 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 6372 Slog.w(TAG, "Restore stream claimed to include apk for " 6373 + info.packageName + " but apk was really " 6374 + mInstallObserver.mPackageName); 6375 // delete the package we just put in place; it might be fraudulent 6376 okay = false; 6377 uninstall = true; 6378 } else { 6379 try { 6380 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 6381 PackageManager.GET_SIGNATURES); 6382 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 6383 Slog.w(TAG, "Restore stream contains apk of package " 6384 + info.packageName + " but it disallows backup/restore"); 6385 okay = false; 6386 } else { 6387 // So far so good -- do the signatures match the manifest? 6388 Signature[] sigs = mManifestSignatures.get(info.packageName); 6389 if (signaturesMatch(sigs, pkg)) { 6390 // If this is a system-uid app without a declared backup agent, 6391 // don't restore any of the file data. 6392 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 6393 && (pkg.applicationInfo.backupAgentName == null)) { 6394 Slog.w(TAG, "Installed app " + info.packageName 6395 + " has restricted uid and no agent"); 6396 okay = false; 6397 } 6398 } else { 6399 Slog.w(TAG, "Installed app " + info.packageName 6400 + " signatures do not match restore manifest"); 6401 okay = false; 6402 uninstall = true; 6403 } 6404 } 6405 } catch (NameNotFoundException e) { 6406 Slog.w(TAG, "Install of package " + info.packageName 6407 + " succeeded but now not found"); 6408 okay = false; 6409 } 6410 } 6411 6412 // If we're not okay at this point, we need to delete the package 6413 // that we just installed. 6414 if (uninstall) { 6415 mDeleteObserver.reset(); 6416 mPackageManager.deletePackage(mInstallObserver.mPackageName, 6417 mDeleteObserver, 0); 6418 mDeleteObserver.waitForCompletion(); 6419 } 6420 } 6421 } catch (IOException e) { 6422 Slog.e(TAG, "Unable to transcribe restored apk for install"); 6423 okay = false; 6424 } finally { 6425 apkFile.delete(); 6426 } 6427 6428 return okay; 6429 } 6430 6431 // Given an actual file content size, consume the post-content padding mandated 6432 // by the tar format. 6433 void skipTarPadding(long size, InputStream instream) throws IOException { 6434 long partial = (size + 512) % 512; 6435 if (partial > 0) { 6436 final int needed = 512 - (int)partial; 6437 byte[] buffer = new byte[needed]; 6438 if (readExactly(instream, buffer, 0, needed) == needed) { 6439 mBytes += needed; 6440 } else throw new IOException("Unexpected EOF in padding"); 6441 } 6442 } 6443 6444 // Read a widget metadata file, returning the restored blob 6445 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 6446 // Fail on suspiciously large widget dump files 6447 if (info.size > 64 * 1024) { 6448 throw new IOException("Metadata too big; corrupt? size=" + info.size); 6449 } 6450 6451 byte[] buffer = new byte[(int) info.size]; 6452 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 6453 mBytes += info.size; 6454 } else throw new IOException("Unexpected EOF in widget data"); 6455 6456 String[] str = new String[1]; 6457 int offset = extractLine(buffer, 0, str); 6458 int version = Integer.parseInt(str[0]); 6459 if (version == BACKUP_MANIFEST_VERSION) { 6460 offset = extractLine(buffer, offset, str); 6461 final String pkg = str[0]; 6462 if (info.packageName.equals(pkg)) { 6463 // Data checks out -- the rest of the buffer is a concatenation of 6464 // binary blobs as described in the comment at writeAppWidgetData() 6465 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 6466 offset, buffer.length - offset); 6467 DataInputStream in = new DataInputStream(bin); 6468 while (bin.available() > 0) { 6469 int token = in.readInt(); 6470 int size = in.readInt(); 6471 if (size > 64 * 1024) { 6472 throw new IOException("Datum " 6473 + Integer.toHexString(token) 6474 + " too big; corrupt? size=" + info.size); 6475 } 6476 switch (token) { 6477 case BACKUP_WIDGET_METADATA_TOKEN: 6478 { 6479 if (MORE_DEBUG) { 6480 Slog.i(TAG, "Got widget metadata for " + info.packageName); 6481 } 6482 mWidgetData = new byte[size]; 6483 in.read(mWidgetData); 6484 break; 6485 } 6486 default: 6487 { 6488 if (DEBUG) { 6489 Slog.i(TAG, "Ignoring metadata blob " 6490 + Integer.toHexString(token) 6491 + " for " + info.packageName); 6492 } 6493 in.skipBytes(size); 6494 break; 6495 } 6496 } 6497 } 6498 } else { 6499 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 6500 + " but widget data for " + pkg); 6501 } 6502 } else { 6503 Slog.w(TAG, "Unsupported metadata version " + version); 6504 } 6505 } 6506 6507 // Returns a policy constant; takes a buffer arg to reduce memory churn 6508 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 6509 throws IOException { 6510 // Fail on suspiciously large manifest files 6511 if (info.size > 64 * 1024) { 6512 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 6513 } 6514 6515 byte[] buffer = new byte[(int) info.size]; 6516 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 6517 mBytes += info.size; 6518 } else throw new IOException("Unexpected EOF in manifest"); 6519 6520 RestorePolicy policy = RestorePolicy.IGNORE; 6521 String[] str = new String[1]; 6522 int offset = 0; 6523 6524 try { 6525 offset = extractLine(buffer, offset, str); 6526 int version = Integer.parseInt(str[0]); 6527 if (version == BACKUP_MANIFEST_VERSION) { 6528 offset = extractLine(buffer, offset, str); 6529 String manifestPackage = str[0]; 6530 // TODO: handle <original-package> 6531 if (manifestPackage.equals(info.packageName)) { 6532 offset = extractLine(buffer, offset, str); 6533 version = Integer.parseInt(str[0]); // app version 6534 offset = extractLine(buffer, offset, str); 6535 // This is the platform version, which we don't use, but we parse it 6536 // as a safety against corruption in the manifest. 6537 Integer.parseInt(str[0]); 6538 offset = extractLine(buffer, offset, str); 6539 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 6540 offset = extractLine(buffer, offset, str); 6541 boolean hasApk = str[0].equals("1"); 6542 offset = extractLine(buffer, offset, str); 6543 int numSigs = Integer.parseInt(str[0]); 6544 if (numSigs > 0) { 6545 Signature[] sigs = new Signature[numSigs]; 6546 for (int i = 0; i < numSigs; i++) { 6547 offset = extractLine(buffer, offset, str); 6548 sigs[i] = new Signature(str[0]); 6549 } 6550 mManifestSignatures.put(info.packageName, sigs); 6551 6552 // Okay, got the manifest info we need... 6553 try { 6554 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 6555 info.packageName, PackageManager.GET_SIGNATURES); 6556 // Fall through to IGNORE if the app explicitly disallows backup 6557 final int flags = pkgInfo.applicationInfo.flags; 6558 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 6559 // Restore system-uid-space packages only if they have 6560 // defined a custom backup agent 6561 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 6562 || (pkgInfo.applicationInfo.backupAgentName != null)) { 6563 // Verify signatures against any installed version; if they 6564 // don't match, then we fall though and ignore the data. The 6565 // signatureMatch() method explicitly ignores the signature 6566 // check for packages installed on the system partition, because 6567 // such packages are signed with the platform cert instead of 6568 // the app developer's cert, so they're different on every 6569 // device. 6570 if (signaturesMatch(sigs, pkgInfo)) { 6571 if (pkgInfo.versionCode >= version) { 6572 Slog.i(TAG, "Sig + version match; taking data"); 6573 policy = RestorePolicy.ACCEPT; 6574 } else { 6575 // The data is from a newer version of the app than 6576 // is presently installed. That means we can only 6577 // use it if the matching apk is also supplied. 6578 Slog.d(TAG, "Data version " + version 6579 + " is newer than installed version " 6580 + pkgInfo.versionCode + " - requiring apk"); 6581 policy = RestorePolicy.ACCEPT_IF_APK; 6582 } 6583 } else { 6584 Slog.w(TAG, "Restore manifest signatures do not match " 6585 + "installed application for " + info.packageName); 6586 } 6587 } else { 6588 Slog.w(TAG, "Package " + info.packageName 6589 + " is system level with no agent"); 6590 } 6591 } else { 6592 if (DEBUG) Slog.i(TAG, "Restore manifest from " 6593 + info.packageName + " but allowBackup=false"); 6594 } 6595 } catch (NameNotFoundException e) { 6596 // Okay, the target app isn't installed. We can process 6597 // the restore properly only if the dataset provides the 6598 // apk file and we can successfully install it. 6599 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 6600 + " not installed; requiring apk in dataset"); 6601 policy = RestorePolicy.ACCEPT_IF_APK; 6602 } 6603 6604 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 6605 Slog.i(TAG, "Cannot restore package " + info.packageName 6606 + " without the matching .apk"); 6607 } 6608 } else { 6609 Slog.i(TAG, "Missing signature on backed-up package " 6610 + info.packageName); 6611 } 6612 } else { 6613 Slog.i(TAG, "Expected package " + info.packageName 6614 + " but restore manifest claims " + manifestPackage); 6615 } 6616 } else { 6617 Slog.i(TAG, "Unknown restore manifest version " + version 6618 + " for package " + info.packageName); 6619 } 6620 } catch (NumberFormatException e) { 6621 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 6622 } catch (IllegalArgumentException e) { 6623 Slog.w(TAG, e.getMessage()); 6624 } 6625 6626 return policy; 6627 } 6628 6629 // Builds a line from a byte buffer starting at 'offset', and returns 6630 // the index of the next unconsumed data in the buffer. 6631 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 6632 final int end = buffer.length; 6633 if (offset >= end) throw new IOException("Incomplete data"); 6634 6635 int pos; 6636 for (pos = offset; pos < end; pos++) { 6637 byte c = buffer[pos]; 6638 // at LF we declare end of line, and return the next char as the 6639 // starting point for the next time through 6640 if (c == '\n') { 6641 break; 6642 } 6643 } 6644 outStr[0] = new String(buffer, offset, pos - offset); 6645 pos++; // may be pointing an extra byte past the end but that's okay 6646 return pos; 6647 } 6648 6649 void dumpFileMetadata(FileMetadata info) { 6650 if (DEBUG) { 6651 StringBuilder b = new StringBuilder(128); 6652 6653 // mode string 6654 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 6655 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 6656 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 6657 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 6658 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 6659 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 6660 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 6661 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 6662 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 6663 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 6664 b.append(String.format(" %9d ", info.size)); 6665 6666 Date stamp = new Date(info.mtime); 6667 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 6668 6669 b.append(info.packageName); 6670 b.append(" :: "); 6671 b.append(info.domain); 6672 b.append(" :: "); 6673 b.append(info.path); 6674 6675 Slog.i(TAG, b.toString()); 6676 } 6677 } 6678 // Consume a tar file header block [sequence] and accumulate the relevant metadata 6679 FileMetadata readTarHeaders(InputStream instream) throws IOException { 6680 byte[] block = new byte[512]; 6681 FileMetadata info = null; 6682 6683 boolean gotHeader = readTarHeader(instream, block); 6684 if (gotHeader) { 6685 try { 6686 // okay, presume we're okay, and extract the various metadata 6687 info = new FileMetadata(); 6688 info.size = extractRadix(block, 124, 12, 8); 6689 info.mtime = extractRadix(block, 136, 12, 8); 6690 info.mode = extractRadix(block, 100, 8, 8); 6691 6692 info.path = extractString(block, 345, 155); // prefix 6693 String path = extractString(block, 0, 100); 6694 if (path.length() > 0) { 6695 if (info.path.length() > 0) info.path += '/'; 6696 info.path += path; 6697 } 6698 6699 // tar link indicator field: 1 byte at offset 156 in the header. 6700 int typeChar = block[156]; 6701 if (typeChar == 'x') { 6702 // pax extended header, so we need to read that 6703 gotHeader = readPaxExtendedHeader(instream, info); 6704 if (gotHeader) { 6705 // and after a pax extended header comes another real header -- read 6706 // that to find the real file type 6707 gotHeader = readTarHeader(instream, block); 6708 } 6709 if (!gotHeader) throw new IOException("Bad or missing pax header"); 6710 6711 typeChar = block[156]; 6712 } 6713 6714 switch (typeChar) { 6715 case '0': info.type = BackupAgent.TYPE_FILE; break; 6716 case '5': { 6717 info.type = BackupAgent.TYPE_DIRECTORY; 6718 if (info.size != 0) { 6719 Slog.w(TAG, "Directory entry with nonzero size in header"); 6720 info.size = 0; 6721 } 6722 break; 6723 } 6724 case 0: { 6725 // presume EOF 6726 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 6727 return null; 6728 } 6729 default: { 6730 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 6731 throw new IOException("Unknown entity type " + typeChar); 6732 } 6733 } 6734 6735 // Parse out the path 6736 // 6737 // first: apps/shared/unrecognized 6738 if (FullBackup.SHARED_PREFIX.regionMatches(0, 6739 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 6740 // File in shared storage. !!! TODO: implement this. 6741 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 6742 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 6743 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 6744 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 6745 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 6746 info.path, 0, FullBackup.APPS_PREFIX.length())) { 6747 // App content! Parse out the package name and domain 6748 6749 // strip the apps/ prefix 6750 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 6751 6752 // extract the package name 6753 int slash = info.path.indexOf('/'); 6754 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 6755 info.packageName = info.path.substring(0, slash); 6756 info.path = info.path.substring(slash+1); 6757 6758 // if it's a manifest or metadata payload we're done, otherwise parse 6759 // out the domain into which the file will be restored 6760 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 6761 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 6762 slash = info.path.indexOf('/'); 6763 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path); 6764 info.domain = info.path.substring(0, slash); 6765 info.path = info.path.substring(slash + 1); 6766 } 6767 } 6768 } catch (IOException e) { 6769 if (DEBUG) { 6770 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 6771 HEXLOG(block); 6772 } 6773 throw e; 6774 } 6775 } 6776 return info; 6777 } 6778 6779 private void HEXLOG(byte[] block) { 6780 int offset = 0; 6781 int todo = block.length; 6782 StringBuilder buf = new StringBuilder(64); 6783 while (todo > 0) { 6784 buf.append(String.format("%04x ", offset)); 6785 int numThisLine = (todo > 16) ? 16 : todo; 6786 for (int i = 0; i < numThisLine; i++) { 6787 buf.append(String.format("%02x ", block[offset+i])); 6788 } 6789 Slog.i("hexdump", buf.toString()); 6790 buf.setLength(0); 6791 todo -= numThisLine; 6792 offset += numThisLine; 6793 } 6794 } 6795 6796 // Read exactly the given number of bytes into a buffer at the stated offset. 6797 // Returns false if EOF is encountered before the requested number of bytes 6798 // could be read. 6799 int readExactly(InputStream in, byte[] buffer, int offset, int size) 6800 throws IOException { 6801 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 6802 6803 int soFar = 0; 6804 while (soFar < size) { 6805 int nRead = in.read(buffer, offset + soFar, size - soFar); 6806 if (nRead <= 0) { 6807 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 6808 break; 6809 } 6810 soFar += nRead; 6811 } 6812 return soFar; 6813 } 6814 6815 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 6816 final int got = readExactly(instream, block, 0, 512); 6817 if (got == 0) return false; // Clean EOF 6818 if (got < 512) throw new IOException("Unable to read full block header"); 6819 mBytes += 512; 6820 return true; 6821 } 6822 6823 // overwrites 'info' fields based on the pax extended header 6824 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 6825 throws IOException { 6826 // We should never see a pax extended header larger than this 6827 if (info.size > 32*1024) { 6828 Slog.w(TAG, "Suspiciously large pax header size " + info.size 6829 + " - aborting"); 6830 throw new IOException("Sanity failure: pax header size " + info.size); 6831 } 6832 6833 // read whole blocks, not just the content size 6834 int numBlocks = (int)((info.size + 511) >> 9); 6835 byte[] data = new byte[numBlocks * 512]; 6836 if (readExactly(instream, data, 0, data.length) < data.length) { 6837 throw new IOException("Unable to read full pax header"); 6838 } 6839 mBytes += data.length; 6840 6841 final int contentSize = (int) info.size; 6842 int offset = 0; 6843 do { 6844 // extract the line at 'offset' 6845 int eol = offset+1; 6846 while (eol < contentSize && data[eol] != ' ') eol++; 6847 if (eol >= contentSize) { 6848 // error: we just hit EOD looking for the end of the size field 6849 throw new IOException("Invalid pax data"); 6850 } 6851 // eol points to the space between the count and the key 6852 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 6853 int key = eol + 1; // start of key=value 6854 eol = offset + linelen - 1; // trailing LF 6855 int value; 6856 for (value = key+1; data[value] != '=' && value <= eol; value++); 6857 if (value > eol) { 6858 throw new IOException("Invalid pax declaration"); 6859 } 6860 6861 // pax requires that key/value strings be in UTF-8 6862 String keyStr = new String(data, key, value-key, "UTF-8"); 6863 // -1 to strip the trailing LF 6864 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 6865 6866 if ("path".equals(keyStr)) { 6867 info.path = valStr; 6868 } else if ("size".equals(keyStr)) { 6869 info.size = Long.parseLong(valStr); 6870 } else { 6871 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 6872 } 6873 6874 offset += linelen; 6875 } while (offset < contentSize); 6876 6877 return true; 6878 } 6879 6880 long extractRadix(byte[] data, int offset, int maxChars, int radix) 6881 throws IOException { 6882 long value = 0; 6883 final int end = offset + maxChars; 6884 for (int i = offset; i < end; i++) { 6885 final byte b = data[i]; 6886 // Numeric fields in tar can terminate with either NUL or SPC 6887 if (b == 0 || b == ' ') break; 6888 if (b < '0' || b > ('0' + radix - 1)) { 6889 throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix); 6890 } 6891 value = radix * value + (b - '0'); 6892 } 6893 return value; 6894 } 6895 6896 String extractString(byte[] data, int offset, int maxChars) throws IOException { 6897 final int end = offset + maxChars; 6898 int eos = offset; 6899 // tar string fields terminate early with a NUL 6900 while (eos < end && data[eos] != 0) eos++; 6901 return new String(data, offset, eos-offset, "US-ASCII"); 6902 } 6903 6904 void sendStartRestore() { 6905 if (mObserver != null) { 6906 try { 6907 mObserver.onStartRestore(); 6908 } catch (RemoteException e) { 6909 Slog.w(TAG, "full restore observer went away: startRestore"); 6910 mObserver = null; 6911 } 6912 } 6913 } 6914 6915 void sendOnRestorePackage(String name) { 6916 if (mObserver != null) { 6917 try { 6918 // TODO: use a more user-friendly name string 6919 mObserver.onRestorePackage(name); 6920 } catch (RemoteException e) { 6921 Slog.w(TAG, "full restore observer went away: restorePackage"); 6922 mObserver = null; 6923 } 6924 } 6925 } 6926 6927 void sendEndRestore() { 6928 if (mObserver != null) { 6929 try { 6930 mObserver.onEndRestore(); 6931 } catch (RemoteException e) { 6932 Slog.w(TAG, "full restore observer went away: endRestore"); 6933 mObserver = null; 6934 } 6935 } 6936 } 6937 } 6938 6939 // ----- Restore handling ----- 6940 6941 // new style: we only store the SHA-1 hashes of each sig, not the full block 6942 static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) { 6943 if (target == null) { 6944 return false; 6945 } 6946 6947 // If the target resides on the system partition, we allow it to restore 6948 // data from the like-named package in a restore set even if the signatures 6949 // do not match. (Unlike general applications, those flashed to the system 6950 // partition will be signed with the device's platform certificate, so on 6951 // different phones the same system app will have different signatures.) 6952 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 6953 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 6954 return true; 6955 } 6956 6957 // Allow unsigned apps, but not signed on one device and unsigned on the other 6958 // !!! TODO: is this the right policy? 6959 Signature[] deviceSigs = target.signatures; 6960 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes 6961 + " device=" + deviceSigs); 6962 if ((storedSigHashes == null || storedSigHashes.size() == 0) 6963 && (deviceSigs == null || deviceSigs.length == 0)) { 6964 return true; 6965 } 6966 if (storedSigHashes == null || deviceSigs == null) { 6967 return false; 6968 } 6969 6970 // !!! TODO: this demands that every stored signature match one 6971 // that is present on device, and does not demand the converse. 6972 // Is this this right policy? 6973 final int nStored = storedSigHashes.size(); 6974 final int nDevice = deviceSigs.length; 6975 6976 // hash each on-device signature 6977 ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice); 6978 for (int i = 0; i < nDevice; i++) { 6979 deviceHashes.add(hashSignature(deviceSigs[i])); 6980 } 6981 6982 // now ensure that each stored sig (hash) matches an on-device sig (hash) 6983 for (int n = 0; n < nStored; n++) { 6984 boolean match = false; 6985 final byte[] storedHash = storedSigHashes.get(n); 6986 for (int i = 0; i < nDevice; i++) { 6987 if (Arrays.equals(storedHash, deviceHashes.get(i))) { 6988 match = true; 6989 break; 6990 } 6991 } 6992 // match is false when no on-device sig matched one of the stored ones 6993 if (!match) { 6994 return false; 6995 } 6996 } 6997 6998 return true; 6999 } 7000 7001 static byte[] hashSignature(Signature sig) { 7002 try { 7003 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 7004 digest.update(sig.toByteArray()); 7005 return digest.digest(); 7006 } catch (NoSuchAlgorithmException e) { 7007 Slog.w(TAG, "No SHA-256 algorithm found!"); 7008 } 7009 return null; 7010 } 7011 7012 // Old style: directly match the stored vs on device signature blocks 7013 static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 7014 if (target == null) { 7015 return false; 7016 } 7017 7018 // If the target resides on the system partition, we allow it to restore 7019 // data from the like-named package in a restore set even if the signatures 7020 // do not match. (Unlike general applications, those flashed to the system 7021 // partition will be signed with the device's platform certificate, so on 7022 // different phones the same system app will have different signatures.) 7023 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 7024 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 7025 return true; 7026 } 7027 7028 // Allow unsigned apps, but not signed on one device and unsigned on the other 7029 // !!! TODO: is this the right policy? 7030 Signature[] deviceSigs = target.signatures; 7031 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs 7032 + " device=" + deviceSigs); 7033 if ((storedSigs == null || storedSigs.length == 0) 7034 && (deviceSigs == null || deviceSigs.length == 0)) { 7035 return true; 7036 } 7037 if (storedSigs == null || deviceSigs == null) { 7038 return false; 7039 } 7040 7041 // !!! TODO: this demands that every stored signature match one 7042 // that is present on device, and does not demand the converse. 7043 // Is this this right policy? 7044 int nStored = storedSigs.length; 7045 int nDevice = deviceSigs.length; 7046 7047 for (int i=0; i < nStored; i++) { 7048 boolean match = false; 7049 for (int j=0; j < nDevice; j++) { 7050 if (storedSigs[i].equals(deviceSigs[j])) { 7051 match = true; 7052 break; 7053 } 7054 } 7055 if (!match) { 7056 return false; 7057 } 7058 } 7059 return true; 7060 } 7061 7062 // Used by both incremental and full restore 7063 void restoreWidgetData(String packageName, byte[] widgetData) { 7064 // Apply the restored widget state and generate the ID update for the app 7065 AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER); 7066 } 7067 7068 // ***************************** 7069 // NEW UNIFIED RESTORE IMPLEMENTATION 7070 // ***************************** 7071 7072 // states of the unified-restore state machine 7073 enum UnifiedRestoreState { 7074 INITIAL, 7075 RUNNING_QUEUE, 7076 RESTORE_KEYVALUE, 7077 RESTORE_FULL, 7078 RESTORE_FINISHED, 7079 FINAL 7080 } 7081 7082 class PerformUnifiedRestoreTask implements BackupRestoreTask { 7083 // Transport we're working with to do the restore 7084 private IBackupTransport mTransport; 7085 7086 // Where per-transport saved state goes 7087 File mStateDir; 7088 7089 // Restore observer; may be null 7090 private IRestoreObserver mObserver; 7091 7092 // Token identifying the dataset to the transport 7093 private long mToken; 7094 7095 // When this is a restore-during-install, this is the token identifying the 7096 // operation to the Package Manager, and we must ensure that we let it know 7097 // when we're finished. 7098 private int mPmToken; 7099 7100 // Is this a whole-system restore, i.e. are we establishing a new ancestral 7101 // dataset to base future restore-at-install operations from? 7102 private boolean mIsSystemRestore; 7103 7104 // If this is a single-package restore, what package are we interested in? 7105 private PackageInfo mTargetPackage; 7106 7107 // In all cases, the calculated list of packages that we are trying to restore 7108 private List<PackageInfo> mAcceptSet; 7109 7110 // Our bookkeeping about the ancestral dataset 7111 private PackageManagerBackupAgent mPmAgent; 7112 7113 // Currently-bound backup agent for restore + restoreFinished purposes 7114 private IBackupAgent mAgent; 7115 7116 // What sort of restore we're doing now 7117 private RestoreDescription mRestoreDescription; 7118 7119 // The package we're currently restoring 7120 private PackageInfo mCurrentPackage; 7121 7122 // Widget-related data handled as part of this restore operation 7123 private byte[] mWidgetData; 7124 7125 // Number of apps restored in this pass 7126 private int mCount; 7127 7128 // When did we start? 7129 private long mStartRealtime; 7130 7131 // State machine progress 7132 private UnifiedRestoreState mState; 7133 7134 // How are things going? 7135 private int mStatus; 7136 7137 // Done? 7138 private boolean mFinished; 7139 7140 // Key/value: bookkeeping about staged data and files for agent access 7141 private File mBackupDataName; 7142 private File mStageName; 7143 private File mSavedStateName; 7144 private File mNewStateName; 7145 ParcelFileDescriptor mBackupData; 7146 ParcelFileDescriptor mNewState; 7147 7148 // Invariant: mWakelock is already held, and this task is responsible for 7149 // releasing it at the end of the restore operation. 7150 PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer, 7151 long restoreSetToken, PackageInfo targetPackage, int pmToken, 7152 boolean isFullSystemRestore, String[] filterSet) { 7153 mState = UnifiedRestoreState.INITIAL; 7154 mStartRealtime = SystemClock.elapsedRealtime(); 7155 7156 mTransport = transport; 7157 mObserver = observer; 7158 mToken = restoreSetToken; 7159 mPmToken = pmToken; 7160 mTargetPackage = targetPackage; 7161 mIsSystemRestore = isFullSystemRestore; 7162 mFinished = false; 7163 7164 if (targetPackage != null) { 7165 // Single package restore 7166 mAcceptSet = new ArrayList<PackageInfo>(); 7167 mAcceptSet.add(targetPackage); 7168 } else { 7169 // Everything possible, or a target set 7170 if (filterSet == null) { 7171 // We want everything and a pony 7172 List<PackageInfo> apps = 7173 PackageManagerBackupAgent.getStorableApplications(mPackageManager); 7174 filterSet = packagesToNames(apps); 7175 if (DEBUG) { 7176 Slog.i(TAG, "Full restore; asking for " + filterSet.length + " apps"); 7177 } 7178 } 7179 7180 mAcceptSet = new ArrayList<PackageInfo>(filterSet.length); 7181 7182 // Pro tem, we insist on moving the settings provider package to last place. 7183 // Keep track of whether it's in the list, and bump it down if so. We also 7184 // want to do the system package itself first if it's called for. 7185 boolean hasSystem = false; 7186 boolean hasSettings = false; 7187 for (int i = 0; i < filterSet.length; i++) { 7188 try { 7189 PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0); 7190 if ("android".equals(info.packageName)) { 7191 hasSystem = true; 7192 continue; 7193 } 7194 if (SETTINGS_PACKAGE.equals(info.packageName)) { 7195 hasSettings = true; 7196 continue; 7197 } 7198 7199 if (appIsEligibleForBackup(info.applicationInfo)) { 7200 mAcceptSet.add(info); 7201 } 7202 } catch (NameNotFoundException e) { 7203 // requested package name doesn't exist; ignore it 7204 } 7205 } 7206 if (hasSystem) { 7207 try { 7208 mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0)); 7209 } catch (NameNotFoundException e) { 7210 // won't happen; we know a priori that it's valid 7211 } 7212 } 7213 if (hasSettings) { 7214 try { 7215 mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0)); 7216 } catch (NameNotFoundException e) { 7217 // this one is always valid too 7218 } 7219 } 7220 } 7221 7222 if (MORE_DEBUG) { 7223 Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size()); 7224 for (PackageInfo info : mAcceptSet) { 7225 Slog.v(TAG, " " + info.packageName); 7226 } 7227 } 7228 } 7229 7230 private String[] packagesToNames(List<PackageInfo> apps) { 7231 final int N = apps.size(); 7232 String[] names = new String[N]; 7233 for (int i = 0; i < N; i++) { 7234 names[i] = apps.get(i).packageName; 7235 } 7236 return names; 7237 } 7238 7239 // Execute one tick of whatever state machine the task implements 7240 @Override 7241 public void execute() { 7242 if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState); 7243 switch (mState) { 7244 case INITIAL: 7245 startRestore(); 7246 break; 7247 7248 case RUNNING_QUEUE: 7249 dispatchNextRestore(); 7250 break; 7251 7252 case RESTORE_KEYVALUE: 7253 restoreKeyValue(); 7254 break; 7255 7256 case RESTORE_FULL: 7257 restoreFull(); 7258 break; 7259 7260 case RESTORE_FINISHED: 7261 restoreFinished(); 7262 break; 7263 7264 case FINAL: 7265 if (!mFinished) finalizeRestore(); 7266 else { 7267 Slog.e(TAG, "Duplicate finish"); 7268 } 7269 mFinished = true; 7270 break; 7271 } 7272 } 7273 7274 /* 7275 * SKETCH OF OPERATION 7276 * 7277 * create one of these PerformUnifiedRestoreTask objects, telling it which 7278 * dataset & transport to address, and then parameters within the restore 7279 * operation: single target package vs many, etc. 7280 * 7281 * 1. transport.startRestore(token, list-of-packages). If we need @pm@ it is 7282 * always placed first and the settings provider always placed last [for now]. 7283 * 7284 * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline] 7285 * 7286 * [ state change => RUNNING_QUEUE ] 7287 * 7288 * NOW ITERATE: 7289 * 7290 * { 3. t.nextRestorePackage() 7291 * 4. does the metadata for this package allow us to restore it? 7292 * does the on-disk app permit us to restore it? [re-check allowBackup etc] 7293 * 5. is this a key/value dataset? => key/value agent restore 7294 * [ state change => RESTORE_KEYVALUE ] 7295 * 5a. spin up agent 7296 * 5b. t.getRestoreData() to stage it properly 7297 * 5c. call into agent to perform restore 7298 * 5d. tear down agent 7299 * [ state change => RUNNING_QUEUE ] 7300 * 7301 * 6. else it's a stream dataset: 7302 * [ state change => RESTORE_FULL ] 7303 * 6a. instantiate the engine for a stream restore: engine handles agent lifecycles 7304 * 6b. spin off engine runner on separate thread 7305 * 6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket 7306 * [ state change => RUNNING_QUEUE ] 7307 * } 7308 * 7309 * [ state change => FINAL ] 7310 * 7311 * 7. t.finishRestore(), release wakelock, etc. 7312 * 7313 * 7314 */ 7315 7316 // state INITIAL : set up for the restore and read the metadata if necessary 7317 private void startRestore() { 7318 sendStartRestore(mAcceptSet.size()); 7319 7320 // If we're starting a full-system restore, set up to begin widget ID remapping 7321 if (mIsSystemRestore) { 7322 AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER); 7323 } 7324 7325 try { 7326 String transportDir = mTransport.transportDirName(); 7327 mStateDir = new File(mBaseStateDir, transportDir); 7328 7329 // Fetch the current metadata from the dataset first 7330 PackageInfo pmPackage = new PackageInfo(); 7331 pmPackage.packageName = PACKAGE_MANAGER_SENTINEL; 7332 mAcceptSet.add(0, pmPackage); 7333 7334 PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]); 7335 mStatus = mTransport.startRestore(mToken, packages); 7336 if (mStatus != BackupTransport.TRANSPORT_OK) { 7337 Slog.e(TAG, "Transport error " + mStatus + "; no restore possible"); 7338 mStatus = BackupTransport.TRANSPORT_ERROR; 7339 executeNextState(UnifiedRestoreState.FINAL); 7340 return; 7341 } 7342 7343 RestoreDescription desc = mTransport.nextRestorePackage(); 7344 if (desc == null) { 7345 Slog.e(TAG, "No restore metadata available; halting"); 7346 mStatus = BackupTransport.TRANSPORT_ERROR; 7347 executeNextState(UnifiedRestoreState.FINAL); 7348 return; 7349 } 7350 if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) { 7351 Slog.e(TAG, "Required metadata but got " + desc.getPackageName()); 7352 mStatus = BackupTransport.TRANSPORT_ERROR; 7353 executeNextState(UnifiedRestoreState.FINAL); 7354 return; 7355 } 7356 7357 // Pull the Package Manager metadata from the restore set first 7358 mCurrentPackage = new PackageInfo(); 7359 mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL; 7360 mPmAgent = new PackageManagerBackupAgent(mPackageManager, null); 7361 mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind()); 7362 if (MORE_DEBUG) { 7363 Slog.v(TAG, "initiating restore for PMBA"); 7364 } 7365 initiateOneRestore(mCurrentPackage, 0); 7366 // The PM agent called operationComplete() already, because our invocation 7367 // of it is process-local and therefore synchronous. That means that the 7368 // next-state message (RUNNING_QUEUE) is already enqueued. Only if we're 7369 // unable to proceed with running the queue do we remove that pending 7370 // message and jump straight to the FINAL state. 7371 7372 // Verify that the backup set includes metadata. If not, we can't do 7373 // signature/version verification etc, so we simply do not proceed with 7374 // the restore operation. 7375 if (!mPmAgent.hasMetadata()) { 7376 Slog.e(TAG, "No restore metadata available, so not restoring"); 7377 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7378 PACKAGE_MANAGER_SENTINEL, 7379 "Package manager restore metadata missing"); 7380 mStatus = BackupTransport.TRANSPORT_ERROR; 7381 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 7382 executeNextState(UnifiedRestoreState.FINAL); 7383 return; 7384 } 7385 7386 // Success; cache the metadata and continue as expected with the 7387 // next state already enqueued 7388 7389 } catch (RemoteException e) { 7390 // If we lost the transport at any time, halt 7391 Slog.e(TAG, "Unable to contact transport for restore"); 7392 mStatus = BackupTransport.TRANSPORT_ERROR; 7393 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 7394 executeNextState(UnifiedRestoreState.FINAL); 7395 return; 7396 } 7397 } 7398 7399 // state RUNNING_QUEUE : figure out what the next thing to be restored is, 7400 // and fire the appropriate next step 7401 private void dispatchNextRestore() { 7402 UnifiedRestoreState nextState = UnifiedRestoreState.FINAL; 7403 try { 7404 mRestoreDescription = mTransport.nextRestorePackage(); 7405 final String pkgName = (mRestoreDescription != null) 7406 ? mRestoreDescription.getPackageName() : null; 7407 if (pkgName == null) { 7408 Slog.e(TAG, "Failure getting next package name"); 7409 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7410 nextState = UnifiedRestoreState.FINAL; 7411 return; 7412 } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) { 7413 // Yay we've reached the end cleanly 7414 if (DEBUG) { 7415 Slog.v(TAG, "No more packages; finishing restore"); 7416 } 7417 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); 7418 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis); 7419 nextState = UnifiedRestoreState.FINAL; 7420 return; 7421 } 7422 7423 if (DEBUG) { 7424 Slog.i(TAG, "Next restore package: " + mRestoreDescription); 7425 } 7426 sendOnRestorePackage(pkgName); 7427 7428 Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName); 7429 if (metaInfo == null) { 7430 Slog.e(TAG, "No metadata for " + pkgName); 7431 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 7432 "Package metadata missing"); 7433 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7434 return; 7435 } 7436 7437 try { 7438 mCurrentPackage = mPackageManager.getPackageInfo( 7439 pkgName, PackageManager.GET_SIGNATURES); 7440 } catch (NameNotFoundException e) { 7441 // Whoops, we thought we could restore this package but it 7442 // turns out not to be present. Skip it. 7443 Slog.e(TAG, "Package not present: " + pkgName); 7444 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 7445 "Package missing on device"); 7446 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7447 return; 7448 } 7449 7450 if (metaInfo.versionCode > mCurrentPackage.versionCode) { 7451 // Data is from a "newer" version of the app than we have currently 7452 // installed. If the app has not declared that it is prepared to 7453 // handle this case, we do not attempt the restore. 7454 if ((mCurrentPackage.applicationInfo.flags 7455 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { 7456 String message = "Version " + metaInfo.versionCode 7457 + " > installed version " + mCurrentPackage.versionCode; 7458 Slog.w(TAG, "Package " + pkgName + ": " + message); 7459 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7460 pkgName, message); 7461 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7462 return; 7463 } else { 7464 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode 7465 + " > installed " + mCurrentPackage.versionCode 7466 + " but restoreAnyVersion"); 7467 } 7468 } 7469 7470 if (DEBUG) Slog.v(TAG, "Package " + pkgName 7471 + " restore version [" + metaInfo.versionCode 7472 + "] is compatible with installed version [" 7473 + mCurrentPackage.versionCode + "]"); 7474 7475 // Reset per-package preconditions and fire the appropriate next state 7476 mWidgetData = null; 7477 final int type = mRestoreDescription.getDataType(); 7478 if (type == RestoreDescription.TYPE_KEY_VALUE) { 7479 nextState = UnifiedRestoreState.RESTORE_KEYVALUE; 7480 } else if (type == RestoreDescription.TYPE_FULL_STREAM) { 7481 nextState = UnifiedRestoreState.RESTORE_FULL; 7482 } else { 7483 // Unknown restore type; ignore this package and move on 7484 Slog.e(TAG, "Unrecognized restore type " + type); 7485 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7486 return; 7487 } 7488 } catch (RemoteException e) { 7489 Slog.e(TAG, "Can't get next target from transport; ending restore"); 7490 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7491 nextState = UnifiedRestoreState.FINAL; 7492 return; 7493 } finally { 7494 executeNextState(nextState); 7495 } 7496 } 7497 7498 // state RESTORE_KEYVALUE : restore one package via key/value API set 7499 private void restoreKeyValue() { 7500 // Initiating the restore will pass responsibility for the state machine's 7501 // progress to the agent callback, so we do not always execute the 7502 // next state here. 7503 final String packageName = mCurrentPackage.packageName; 7504 // Validate some semantic requirements that apply in this way 7505 // only to the key/value restore API flow 7506 if (mCurrentPackage.applicationInfo.backupAgentName == null 7507 || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) { 7508 if (DEBUG) { 7509 Slog.i(TAG, "Data exists for package " + packageName 7510 + " but app has no agent; skipping"); 7511 } 7512 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 7513 "Package has no agent"); 7514 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7515 return; 7516 } 7517 7518 Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); 7519 if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { 7520 Slog.w(TAG, "Signature mismatch restoring " + packageName); 7521 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 7522 "Signature mismatch"); 7523 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7524 return; 7525 } 7526 7527 // Good to go! Set up and bind the agent... 7528 mAgent = bindToAgentSynchronous( 7529 mCurrentPackage.applicationInfo, 7530 IApplicationThread.BACKUP_MODE_INCREMENTAL); 7531 if (mAgent == null) { 7532 Slog.w(TAG, "Can't find backup agent for " + packageName); 7533 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 7534 "Restore agent missing"); 7535 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7536 return; 7537 } 7538 7539 // And then finally start the restore on this agent 7540 try { 7541 initiateOneRestore(mCurrentPackage, metaInfo.versionCode); 7542 ++mCount; 7543 } catch (Exception e) { 7544 Slog.e(TAG, "Error when attempting restore: " + e.toString()); 7545 keyValueAgentErrorCleanup(); 7546 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7547 } 7548 } 7549 7550 // Guts of a key/value restore operation 7551 void initiateOneRestore(PackageInfo app, int appVersionCode) { 7552 final String packageName = app.packageName; 7553 7554 if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName); 7555 7556 // !!! TODO: get the dirs from the transport 7557 mBackupDataName = new File(mDataDir, packageName + ".restore"); 7558 mStageName = new File(mDataDir, packageName + ".stage"); 7559 mNewStateName = new File(mStateDir, packageName + ".new"); 7560 mSavedStateName = new File(mStateDir, packageName); 7561 7562 // don't stage the 'android' package where the wallpaper data lives. this is 7563 // an optimization: we know there's no widget data hosted/published by that 7564 // package, and this way we avoid doing a spurious copy of MB-sized wallpaper 7565 // data following the download. 7566 boolean staging = !packageName.equals("android"); 7567 ParcelFileDescriptor stage; 7568 File downloadFile = (staging) ? mStageName : mBackupDataName; 7569 7570 final int token = generateToken(); 7571 try { 7572 // Run the transport's restore pass 7573 stage = ParcelFileDescriptor.open(downloadFile, 7574 ParcelFileDescriptor.MODE_READ_WRITE | 7575 ParcelFileDescriptor.MODE_CREATE | 7576 ParcelFileDescriptor.MODE_TRUNCATE); 7577 7578 if (!SELinux.restorecon(mBackupDataName)) { 7579 Slog.e(TAG, "SElinux restorecon failed for " + downloadFile); 7580 } 7581 7582 if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) { 7583 // Transport-level failure, so we wind everything up and 7584 // terminate the restore operation. 7585 Slog.e(TAG, "Error getting restore data for " + packageName); 7586 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7587 stage.close(); 7588 downloadFile.delete(); 7589 executeNextState(UnifiedRestoreState.FINAL); 7590 return; 7591 } 7592 7593 // We have the data from the transport. Now we extract and strip 7594 // any per-package metadata (typically widget-related information) 7595 // if appropriate 7596 if (staging) { 7597 stage.close(); 7598 stage = ParcelFileDescriptor.open(downloadFile, 7599 ParcelFileDescriptor.MODE_READ_ONLY); 7600 7601 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 7602 ParcelFileDescriptor.MODE_READ_WRITE | 7603 ParcelFileDescriptor.MODE_CREATE | 7604 ParcelFileDescriptor.MODE_TRUNCATE); 7605 7606 BackupDataInput in = new BackupDataInput(stage.getFileDescriptor()); 7607 BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor()); 7608 byte[] buffer = new byte[8192]; // will grow when needed 7609 while (in.readNextHeader()) { 7610 final String key = in.getKey(); 7611 final int size = in.getDataSize(); 7612 7613 // is this a special key? 7614 if (key.equals(KEY_WIDGET_STATE)) { 7615 if (DEBUG) { 7616 Slog.i(TAG, "Restoring widget state for " + packageName); 7617 } 7618 mWidgetData = new byte[size]; 7619 in.readEntityData(mWidgetData, 0, size); 7620 } else { 7621 if (size > buffer.length) { 7622 buffer = new byte[size]; 7623 } 7624 in.readEntityData(buffer, 0, size); 7625 out.writeEntityHeader(key, size); 7626 out.writeEntityData(buffer, size); 7627 } 7628 } 7629 7630 mBackupData.close(); 7631 } 7632 7633 // Okay, we have the data. Now have the agent do the restore. 7634 stage.close(); 7635 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 7636 ParcelFileDescriptor.MODE_READ_ONLY); 7637 7638 mNewState = ParcelFileDescriptor.open(mNewStateName, 7639 ParcelFileDescriptor.MODE_READ_WRITE | 7640 ParcelFileDescriptor.MODE_CREATE | 7641 ParcelFileDescriptor.MODE_TRUNCATE); 7642 7643 // Kick off the restore, checking for hung agents. The timeout or 7644 // the operationComplete() callback will schedule the next step, 7645 // so we do not do that here. 7646 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this); 7647 mAgent.doRestore(mBackupData, appVersionCode, mNewState, 7648 token, mBackupManagerBinder); 7649 } catch (Exception e) { 7650 Slog.e(TAG, "Unable to call app for restore: " + packageName, e); 7651 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7652 packageName, e.toString()); 7653 keyValueAgentErrorCleanup(); // clears any pending timeout messages as well 7654 7655 // After a restore failure we go back to running the queue. If there 7656 // are no more packages to be restored that will be handled by the 7657 // next step. 7658 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7659 } 7660 } 7661 7662 // state RESTORE_FULL : restore one package via streaming engine 7663 private void restoreFull() { 7664 // None of this can run on the work looper here, so we spin asynchronous 7665 // work like this: 7666 // 7667 // StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk() 7668 // write it into the pipe to the engine 7669 // EngineThread: FullRestoreEngine thread communicating with the target app 7670 // 7671 // When finished, StreamFeederThread executes next state as appropriate on the 7672 // backup looper, and the overall unified restore task resumes 7673 try { 7674 StreamFeederThread feeder = new StreamFeederThread(); 7675 if (DEBUG) { 7676 Slog.i(TAG, "Spinning threads for stream restore of " 7677 + mCurrentPackage.packageName); 7678 } 7679 new Thread(feeder, "unified-stream-feeder").start(); 7680 7681 // At this point the feeder is responsible for advancing the restore 7682 // state, so we're done here. 7683 } catch (IOException e) { 7684 // Unable to instantiate the feeder thread -- we need to bail on the 7685 // current target. We haven't asked the transport for data yet, though, 7686 // so we can do that simply by going back to running the restore queue. 7687 Slog.e(TAG, "Unable to construct pipes for stream restore!"); 7688 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7689 } 7690 } 7691 7692 // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end 7693 private void restoreFinished() { 7694 try { 7695 final int token = generateToken(); 7696 prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this); 7697 mAgent.doRestoreFinished(token, mBackupManagerBinder); 7698 // If we get this far, the callback or timeout will schedule the 7699 // next restore state, so we're done 7700 } catch (Exception e) { 7701 Slog.e(TAG, "Unable to finalize restore of " + mCurrentPackage.packageName); 7702 executeNextState(UnifiedRestoreState.FINAL); 7703 } 7704 } 7705 7706 class StreamFeederThread extends RestoreEngine implements Runnable { 7707 final String TAG = "StreamFeederThread"; 7708 FullRestoreEngine mEngine; 7709 7710 // pipe through which we read data from the transport. [0] read, [1] write 7711 ParcelFileDescriptor[] mTransportPipes; 7712 7713 // pipe through which the engine will read data. [0] read, [1] write 7714 ParcelFileDescriptor[] mEnginePipes; 7715 7716 public StreamFeederThread() throws IOException { 7717 mTransportPipes = ParcelFileDescriptor.createPipe(); 7718 mEnginePipes = ParcelFileDescriptor.createPipe(); 7719 setRunning(true); 7720 } 7721 7722 @Override 7723 public void run() { 7724 UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE; 7725 int status = BackupTransport.TRANSPORT_OK; 7726 7727 EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE, 7728 mCurrentPackage.packageName); 7729 7730 mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false); 7731 EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]); 7732 7733 ParcelFileDescriptor eWriteEnd = mEnginePipes[1]; 7734 ParcelFileDescriptor tReadEnd = mTransportPipes[0]; 7735 ParcelFileDescriptor tWriteEnd = mTransportPipes[1]; 7736 7737 int bufferSize = 32 * 1024; 7738 byte[] buffer = new byte[bufferSize]; 7739 FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor()); 7740 FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor()); 7741 7742 // spin up the engine and start moving data to it 7743 new Thread(eThread, "unified-restore-engine").start(); 7744 7745 try { 7746 while (status == BackupTransport.TRANSPORT_OK) { 7747 // have the transport write some of the restoring data to us 7748 int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd); 7749 if (result > 0) { 7750 // The transport wrote this many bytes of restore data to the 7751 // pipe, so pass it along to the engine. 7752 if (MORE_DEBUG) { 7753 Slog.v(TAG, " <- transport provided chunk size " + result); 7754 } 7755 if (result > bufferSize) { 7756 bufferSize = result; 7757 buffer = new byte[bufferSize]; 7758 } 7759 int toCopy = result; 7760 while (toCopy > 0) { 7761 int n = transportIn.read(buffer, 0, toCopy); 7762 engineOut.write(buffer, 0, n); 7763 toCopy -= n; 7764 if (MORE_DEBUG) { 7765 Slog.v(TAG, " -> wrote " + n + " to engine, left=" + toCopy); 7766 } 7767 } 7768 } else if (result == BackupTransport.NO_MORE_DATA) { 7769 // Clean finish. Wind up and we're done! 7770 if (MORE_DEBUG) { 7771 Slog.i(TAG, "Got clean full-restore EOF for " 7772 + mCurrentPackage.packageName); 7773 } 7774 status = BackupTransport.TRANSPORT_OK; 7775 break; 7776 } else { 7777 // Transport reported some sort of failure; the fall-through 7778 // handling will deal properly with that. 7779 Slog.e(TAG, "Error " + result + " streaming restore for " 7780 + mCurrentPackage.packageName); 7781 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7782 status = result; 7783 } 7784 } 7785 if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through"); 7786 } catch (IOException e) { 7787 // We lost our ability to communicate via the pipes. That's worrying 7788 // but potentially recoverable; abandon this package's restore but 7789 // carry on with the next restore target. 7790 Slog.e(TAG, "Unable to route data for restore"); 7791 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7792 mCurrentPackage.packageName, "I/O error on pipes"); 7793 status = BackupTransport.AGENT_ERROR; 7794 } catch (RemoteException e) { 7795 // The transport went away; terminate the whole operation. Closing 7796 // the sockets will wake up the engine and it will then tidy up the 7797 // remote end. 7798 Slog.e(TAG, "Transport failed during restore"); 7799 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7800 status = BackupTransport.TRANSPORT_ERROR; 7801 } finally { 7802 // Close the transport pipes and *our* end of the engine pipe, 7803 // but leave the engine thread's end open so that it properly 7804 // hits EOF and winds up its operations. 7805 IoUtils.closeQuietly(mEnginePipes[1]); 7806 IoUtils.closeQuietly(mTransportPipes[0]); 7807 IoUtils.closeQuietly(mTransportPipes[1]); 7808 7809 // Don't proceed until the engine has torn down the agent etc 7810 eThread.waitForResult(); 7811 7812 if (MORE_DEBUG) { 7813 Slog.i(TAG, "engine thread finished; proceeding"); 7814 } 7815 7816 // Now we're really done with this one too 7817 IoUtils.closeQuietly(mEnginePipes[0]); 7818 7819 // If we hit a transport-level error, we are done with everything; 7820 // if we hit an agent error we just go back to running the queue. 7821 if (status == BackupTransport.TRANSPORT_OK) { 7822 // Clean finish, so just carry on 7823 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7824 } else { 7825 // Something went wrong somewhere. Whether it was at the transport 7826 // level is immaterial; we need to tell the transport to bail 7827 try { 7828 mTransport.abortFullRestore(); 7829 } catch (RemoteException e) { 7830 // transport itself is dead; make sure we handle this as a 7831 // fatal error 7832 status = BackupTransport.TRANSPORT_ERROR; 7833 } 7834 7835 // We also need to wipe the current target's data, as it's probably 7836 // in an incoherent state. 7837 clearApplicationDataSynchronous(mCurrentPackage.packageName); 7838 7839 // Schedule the next state based on the nature of our failure 7840 if (status == BackupTransport.TRANSPORT_ERROR) { 7841 nextState = UnifiedRestoreState.FINAL; 7842 } else { 7843 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7844 } 7845 } 7846 executeNextState(nextState); 7847 setRunning(false); 7848 } 7849 } 7850 7851 } 7852 7853 class EngineThread implements Runnable { 7854 FullRestoreEngine mEngine; 7855 FileInputStream mEngineStream; 7856 7857 EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) { 7858 mEngine = engine; 7859 engine.setRunning(true); 7860 mEngineStream = new FileInputStream(engineSocket.getFileDescriptor()); 7861 } 7862 7863 public boolean isRunning() { 7864 return mEngine.isRunning(); 7865 } 7866 7867 public int waitForResult() { 7868 return mEngine.waitForResult(); 7869 } 7870 7871 @Override 7872 public void run() { 7873 while (mEngine.isRunning()) { 7874 mEngine.restoreOneFile(mEngineStream); 7875 } 7876 } 7877 } 7878 7879 // state FINAL : tear everything down and we're done. 7880 private void finalizeRestore() { 7881 if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver); 7882 7883 try { 7884 mTransport.finishRestore(); 7885 } catch (Exception e) { 7886 Slog.e(TAG, "Error finishing restore", e); 7887 } 7888 7889 // Tell the observer we're done 7890 if (mObserver != null) { 7891 try { 7892 mObserver.restoreFinished(mStatus); 7893 } catch (RemoteException e) { 7894 Slog.d(TAG, "Restore observer died at restoreFinished"); 7895 } 7896 } 7897 7898 // Clear any ongoing session timeout. 7899 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 7900 7901 // If we have a PM token, we must under all circumstances be sure to 7902 // handshake when we've finished. 7903 if (mPmToken > 0) { 7904 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken); 7905 try { 7906 mPackageManagerBinder.finishPackageInstall(mPmToken); 7907 } catch (RemoteException e) { /* can't happen */ } 7908 } else { 7909 // We were invoked via an active restore session, not by the Package 7910 // Manager, so start up the session timeout again. 7911 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, 7912 TIMEOUT_RESTORE_INTERVAL); 7913 } 7914 7915 // Kick off any work that may be needed regarding app widget restores 7916 AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER); 7917 7918 // If this was a full-system restore, record the ancestral 7919 // dataset information 7920 if (mIsSystemRestore && mPmAgent != null) { 7921 mAncestralPackages = mPmAgent.getRestoredPackages(); 7922 mAncestralToken = mToken; 7923 writeRestoreTokens(); 7924 } 7925 7926 // done; we can finally release the wakelock and be legitimately done. 7927 Slog.i(TAG, "Restore complete."); 7928 mWakelock.release(); 7929 } 7930 7931 void keyValueAgentErrorCleanup() { 7932 // If the agent fails restore, it might have put the app's data 7933 // into an incoherent state. For consistency we wipe its data 7934 // again in this case before continuing with normal teardown 7935 clearApplicationDataSynchronous(mCurrentPackage.packageName); 7936 keyValueAgentCleanup(); 7937 } 7938 7939 void keyValueAgentCleanup() { 7940 mBackupDataName.delete(); 7941 mStageName.delete(); 7942 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 7943 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 7944 mBackupData = mNewState = null; 7945 7946 // if everything went okay, remember the recorded state now 7947 // 7948 // !!! TODO: the restored data could be migrated on the server 7949 // side into the current dataset. In that case the new state file 7950 // we just created would reflect the data already extant in the 7951 // backend, so there'd be nothing more to do. Until that happens, 7952 // however, we need to make sure that we record the data to the 7953 // current backend dataset. (Yes, this means shipping the data over 7954 // the wire in both directions. That's bad, but consistency comes 7955 // first, then efficiency.) Once we introduce server-side data 7956 // migration to the newly-restored device's dataset, we will change 7957 // the following from a discard of the newly-written state to the 7958 // "correct" operation of renaming into the canonical state blob. 7959 mNewStateName.delete(); // TODO: remove; see above comment 7960 //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this 7961 7962 // If this wasn't the PM pseudopackage, tear down the agent side 7963 if (mCurrentPackage.applicationInfo != null) { 7964 // unbind and tidy up even on timeout or failure 7965 try { 7966 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 7967 7968 // The agent was probably running with a stub Application object, 7969 // which isn't a valid run mode for the main app logic. Shut 7970 // down the app so that next time it's launched, it gets the 7971 // usual full initialization. Note that this is only done for 7972 // full-system restores: when a single app has requested a restore, 7973 // it is explicitly not killed following that operation. 7974 if (mTargetPackage == null && (mCurrentPackage.applicationInfo.flags 7975 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) { 7976 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of " 7977 + mCurrentPackage.applicationInfo.processName); 7978 mActivityManager.killApplicationProcess( 7979 mCurrentPackage.applicationInfo.processName, 7980 mCurrentPackage.applicationInfo.uid); 7981 } 7982 } catch (RemoteException e) { 7983 // can't happen; we run in the same process as the activity manager 7984 } 7985 } 7986 7987 // The caller is responsible for reestablishing the state machine; our 7988 // responsibility here is to clear the decks for whatever comes next. 7989 mBackupHandler.removeMessages(MSG_TIMEOUT, this); 7990 synchronized (mCurrentOpLock) { 7991 mCurrentOperations.clear(); 7992 } 7993 } 7994 7995 @Override 7996 public void operationComplete(int unusedResult) { 7997 if (MORE_DEBUG) { 7998 Slog.i(TAG, "operationComplete() during restore: target=" 7999 + mCurrentPackage.packageName 8000 + " state=" + mState); 8001 } 8002 8003 final UnifiedRestoreState nextState; 8004 switch (mState) { 8005 case INITIAL: 8006 // We've just (manually) restored the PMBA. It doesn't need the 8007 // additional restore-finished callback so we bypass that and go 8008 // directly to running the queue. 8009 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8010 break; 8011 8012 case RESTORE_KEYVALUE: 8013 case RESTORE_FULL: { 8014 // Okay, we've just heard back from the agent that it's done with 8015 // the restore itself. We now have to send the same agent its 8016 // doRestoreFinished() callback, so roll into that state. 8017 nextState = UnifiedRestoreState.RESTORE_FINISHED; 8018 break; 8019 } 8020 8021 case RESTORE_FINISHED: { 8022 // Okay, we're done with this package. Tidy up and go on to the next 8023 // app in the queue. 8024 int size = (int) mBackupDataName.length(); 8025 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, 8026 mCurrentPackage.packageName, size); 8027 8028 // Just go back to running the restore queue 8029 keyValueAgentCleanup(); 8030 8031 // If there was widget state associated with this app, get the OS to 8032 // incorporate it into current bookeeping and then pass that along to 8033 // the app as part of the restore-time work. 8034 if (mWidgetData != null) { 8035 restoreWidgetData(mCurrentPackage.packageName, mWidgetData); 8036 } 8037 8038 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8039 break; 8040 } 8041 8042 default: { 8043 // Some kind of horrible semantic error; we're in an unexpected state. 8044 // Back off hard and wind up. 8045 Slog.e(TAG, "Unexpected restore callback into state " + mState); 8046 keyValueAgentErrorCleanup(); 8047 nextState = UnifiedRestoreState.FINAL; 8048 break; 8049 } 8050 } 8051 8052 executeNextState(nextState); 8053 } 8054 8055 // A call to agent.doRestore() or agent.doRestoreFinished() has timed out 8056 @Override 8057 public void handleTimeout() { 8058 Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName); 8059 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8060 mCurrentPackage.packageName, "restore timeout"); 8061 // Handle like an agent that threw on invocation: wipe it and go on to the next 8062 keyValueAgentErrorCleanup(); 8063 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8064 } 8065 8066 void executeNextState(UnifiedRestoreState nextState) { 8067 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 8068 + this + " nextState=" + nextState); 8069 mState = nextState; 8070 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 8071 mBackupHandler.sendMessage(msg); 8072 } 8073 8074 // restore observer support 8075 void sendStartRestore(int numPackages) { 8076 if (mObserver != null) { 8077 try { 8078 mObserver.restoreStarting(numPackages); 8079 } catch (RemoteException e) { 8080 Slog.w(TAG, "Restore observer went away: startRestore"); 8081 mObserver = null; 8082 } 8083 } 8084 } 8085 8086 void sendOnRestorePackage(String name) { 8087 if (mObserver != null) { 8088 if (mObserver != null) { 8089 try { 8090 mObserver.onUpdate(mCount, name); 8091 } catch (RemoteException e) { 8092 Slog.d(TAG, "Restore observer died in onUpdate"); 8093 mObserver = null; 8094 } 8095 } 8096 } 8097 } 8098 8099 void sendEndRestore() { 8100 if (mObserver != null) { 8101 try { 8102 mObserver.restoreFinished(mStatus); 8103 } catch (RemoteException e) { 8104 Slog.w(TAG, "Restore observer went away: endRestore"); 8105 mObserver = null; 8106 } 8107 } 8108 } 8109 } 8110 8111 class PerformClearTask implements Runnable { 8112 IBackupTransport mTransport; 8113 PackageInfo mPackage; 8114 8115 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) { 8116 mTransport = transport; 8117 mPackage = packageInfo; 8118 } 8119 8120 public void run() { 8121 try { 8122 // Clear the on-device backup state to ensure a full backup next time 8123 File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); 8124 File stateFile = new File(stateDir, mPackage.packageName); 8125 stateFile.delete(); 8126 8127 // Tell the transport to remove all the persistent storage for the app 8128 // TODO - need to handle failures 8129 mTransport.clearBackupData(mPackage); 8130 } catch (RemoteException e) { 8131 // can't happen; the transport is local 8132 } catch (Exception e) { 8133 Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage); 8134 } finally { 8135 try { 8136 // TODO - need to handle failures 8137 mTransport.finishBackup(); 8138 } catch (RemoteException e) { 8139 // can't happen; the transport is local 8140 } 8141 8142 // Last but not least, release the cpu 8143 mWakelock.release(); 8144 } 8145 } 8146 } 8147 8148 class PerformInitializeTask implements Runnable { 8149 HashSet<String> mQueue; 8150 8151 PerformInitializeTask(HashSet<String> transportNames) { 8152 mQueue = transportNames; 8153 } 8154 8155 public void run() { 8156 try { 8157 for (String transportName : mQueue) { 8158 IBackupTransport transport = getTransport(transportName); 8159 if (transport == null) { 8160 Slog.e(TAG, "Requested init for " + transportName + " but not found"); 8161 continue; 8162 } 8163 8164 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); 8165 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName()); 8166 long startRealtime = SystemClock.elapsedRealtime(); 8167 int status = transport.initializeDevice(); 8168 8169 if (status == BackupTransport.TRANSPORT_OK) { 8170 status = transport.finishBackup(); 8171 } 8172 8173 // Okay, the wipe really happened. Clean up our local bookkeeping. 8174 if (status == BackupTransport.TRANSPORT_OK) { 8175 Slog.i(TAG, "Device init successful"); 8176 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); 8177 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 8178 resetBackupState(new File(mBaseStateDir, transport.transportDirName())); 8179 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); 8180 synchronized (mQueueLock) { 8181 recordInitPendingLocked(false, transportName); 8182 } 8183 } else { 8184 // If this didn't work, requeue this one and try again 8185 // after a suitable interval 8186 Slog.e(TAG, "Transport error in initializeDevice()"); 8187 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 8188 synchronized (mQueueLock) { 8189 recordInitPendingLocked(true, transportName); 8190 } 8191 // do this via another alarm to make sure of the wakelock states 8192 long delay = transport.requestBackupTime(); 8193 if (DEBUG) Slog.w(TAG, "init failed on " 8194 + transportName + " resched in " + delay); 8195 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 8196 System.currentTimeMillis() + delay, mRunInitIntent); 8197 } 8198 } 8199 } catch (RemoteException e) { 8200 // can't happen; the transports are local 8201 } catch (Exception e) { 8202 Slog.e(TAG, "Unexpected error performing init", e); 8203 } finally { 8204 // Done; release the wakelock 8205 mWakelock.release(); 8206 } 8207 } 8208 } 8209 8210 private void dataChangedImpl(String packageName) { 8211 HashSet<String> targets = dataChangedTargets(packageName); 8212 dataChangedImpl(packageName, targets); 8213 } 8214 8215 private void dataChangedImpl(String packageName, HashSet<String> targets) { 8216 // Record that we need a backup pass for the caller. Since multiple callers 8217 // may share a uid, we need to note all candidates within that uid and schedule 8218 // a backup pass for each of them. 8219 if (targets == null) { 8220 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 8221 + " uid=" + Binder.getCallingUid()); 8222 return; 8223 } 8224 8225 synchronized (mQueueLock) { 8226 // Note that this client has made data changes that need to be backed up 8227 if (targets.contains(packageName)) { 8228 // Add the caller to the set of pending backups. If there is 8229 // one already there, then overwrite it, but no harm done. 8230 BackupRequest req = new BackupRequest(packageName); 8231 if (mPendingBackups.put(packageName, req) == null) { 8232 if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName); 8233 8234 // Journal this request in case of crash. The put() 8235 // operation returned null when this package was not already 8236 // in the set; we want to avoid touching the disk redundantly. 8237 writeToJournalLocked(packageName); 8238 8239 if (MORE_DEBUG) { 8240 int numKeys = mPendingBackups.size(); 8241 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); 8242 for (BackupRequest b : mPendingBackups.values()) { 8243 Slog.d(TAG, " + " + b); 8244 } 8245 } 8246 } 8247 } 8248 } 8249 8250 // ...and schedule a backup pass if necessary 8251 KeyValueBackupJob.schedule(mContext); 8252 } 8253 8254 // Note: packageName is currently unused, but may be in the future 8255 private HashSet<String> dataChangedTargets(String packageName) { 8256 // If the caller does not hold the BACKUP permission, it can only request a 8257 // backup of its own data. 8258 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 8259 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 8260 synchronized (mBackupParticipants) { 8261 return mBackupParticipants.get(Binder.getCallingUid()); 8262 } 8263 } 8264 8265 // a caller with full permission can ask to back up any participating app 8266 // !!! TODO: allow backup of ANY app? 8267 HashSet<String> targets = new HashSet<String>(); 8268 synchronized (mBackupParticipants) { 8269 int N = mBackupParticipants.size(); 8270 for (int i = 0; i < N; i++) { 8271 HashSet<String> s = mBackupParticipants.valueAt(i); 8272 if (s != null) { 8273 targets.addAll(s); 8274 } 8275 } 8276 } 8277 return targets; 8278 } 8279 8280 private void writeToJournalLocked(String str) { 8281 RandomAccessFile out = null; 8282 try { 8283 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir); 8284 out = new RandomAccessFile(mJournal, "rws"); 8285 out.seek(out.length()); 8286 out.writeUTF(str); 8287 } catch (IOException e) { 8288 Slog.e(TAG, "Can't write " + str + " to backup journal", e); 8289 mJournal = null; 8290 } finally { 8291 try { if (out != null) out.close(); } catch (IOException e) {} 8292 } 8293 } 8294 8295 // ----- IBackupManager binder interface ----- 8296 8297 public void dataChanged(final String packageName) { 8298 final int callingUserHandle = UserHandle.getCallingUserId(); 8299 if (callingUserHandle != UserHandle.USER_OWNER) { 8300 // App is running under a non-owner user profile. For now, we do not back 8301 // up data from secondary user profiles. 8302 // TODO: backups for all user profiles. 8303 if (MORE_DEBUG) { 8304 Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " 8305 + callingUserHandle); 8306 } 8307 return; 8308 } 8309 8310 final HashSet<String> targets = dataChangedTargets(packageName); 8311 if (targets == null) { 8312 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 8313 + " uid=" + Binder.getCallingUid()); 8314 return; 8315 } 8316 8317 mBackupHandler.post(new Runnable() { 8318 public void run() { 8319 dataChangedImpl(packageName, targets); 8320 } 8321 }); 8322 } 8323 8324 // Clear the given package's backup data from the current transport 8325 public void clearBackupData(String transportName, String packageName) { 8326 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); 8327 PackageInfo info; 8328 try { 8329 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 8330 } catch (NameNotFoundException e) { 8331 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); 8332 return; 8333 } 8334 8335 // If the caller does not hold the BACKUP permission, it can only request a 8336 // wipe of its own backed-up data. 8337 HashSet<String> apps; 8338 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 8339 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 8340 apps = mBackupParticipants.get(Binder.getCallingUid()); 8341 } else { 8342 // a caller with full permission can ask to back up any participating app 8343 // !!! TODO: allow data-clear of ANY app? 8344 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps"); 8345 apps = new HashSet<String>(); 8346 int N = mBackupParticipants.size(); 8347 for (int i = 0; i < N; i++) { 8348 HashSet<String> s = mBackupParticipants.valueAt(i); 8349 if (s != null) { 8350 apps.addAll(s); 8351 } 8352 } 8353 } 8354 8355 // Is the given app an available participant? 8356 if (apps.contains(packageName)) { 8357 // found it; fire off the clear request 8358 if (DEBUG) Slog.v(TAG, "Found the app - running clear process"); 8359 mBackupHandler.removeMessages(MSG_RETRY_CLEAR); 8360 synchronized (mQueueLock) { 8361 final IBackupTransport transport = getTransport(transportName); 8362 if (transport == null) { 8363 // transport is currently unavailable -- make sure to retry 8364 Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR, 8365 new ClearRetryParams(transportName, packageName)); 8366 mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL); 8367 return; 8368 } 8369 long oldId = Binder.clearCallingIdentity(); 8370 mWakelock.acquire(); 8371 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, 8372 new ClearParams(transport, info)); 8373 mBackupHandler.sendMessage(msg); 8374 Binder.restoreCallingIdentity(oldId); 8375 } 8376 } 8377 } 8378 8379 // Run a backup pass immediately for any applications that have declared 8380 // that they have pending updates. 8381 public void backupNow() { 8382 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow"); 8383 8384 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); 8385 synchronized (mQueueLock) { 8386 // Fire the intent that kicks off the whole shebang... 8387 try { 8388 mRunBackupIntent.send(); 8389 } catch (PendingIntent.CanceledException e) { 8390 // should never happen 8391 Slog.e(TAG, "run-backup intent cancelled!"); 8392 } 8393 8394 // ...and cancel any pending scheduled job, because we've just superseded it 8395 KeyValueBackupJob.cancel(mContext); 8396 } 8397 } 8398 8399 boolean deviceIsProvisioned() { 8400 final ContentResolver resolver = mContext.getContentResolver(); 8401 return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); 8402 } 8403 8404 // Run a *full* backup pass for the given packages, writing the resulting data stream 8405 // to the supplied file descriptor. This method is synchronous and does not return 8406 // to the caller until the backup has been completed. 8407 // 8408 // This is the variant used by 'adb backup'; it requires on-screen confirmation 8409 // by the user because it can be used to offload data over untrusted USB. 8410 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, 8411 boolean includeObbs, boolean includeShared, boolean doWidgets, 8412 boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) { 8413 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); 8414 8415 final int callingUserHandle = UserHandle.getCallingUserId(); 8416 if (callingUserHandle != UserHandle.USER_OWNER) { 8417 throw new IllegalStateException("Backup supported only for the device owner"); 8418 } 8419 8420 // Validate 8421 if (!doAllApps) { 8422 if (!includeShared) { 8423 // If we're backing up shared data (sdcard or equivalent), then we can run 8424 // without any supplied app names. Otherwise, we'd be doing no work, so 8425 // report the error. 8426 if (pkgList == null || pkgList.length == 0) { 8427 throw new IllegalArgumentException( 8428 "Backup requested but neither shared nor any apps named"); 8429 } 8430 } 8431 } 8432 8433 long oldId = Binder.clearCallingIdentity(); 8434 try { 8435 // Doesn't make sense to do a full backup prior to setup 8436 if (!deviceIsProvisioned()) { 8437 Slog.i(TAG, "Full backup not supported before setup"); 8438 return; 8439 } 8440 8441 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks 8442 + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps 8443 + " system=" + includeSystem + " pkgs=" + pkgList); 8444 Slog.i(TAG, "Beginning full backup..."); 8445 8446 FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs, 8447 includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList); 8448 final int token = generateToken(); 8449 synchronized (mFullConfirmations) { 8450 mFullConfirmations.put(token, params); 8451 } 8452 8453 // start up the confirmation UI 8454 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token); 8455 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { 8456 Slog.e(TAG, "Unable to launch full backup confirmation"); 8457 mFullConfirmations.delete(token); 8458 return; 8459 } 8460 8461 // make sure the screen is lit for the user interaction 8462 mPowerManager.userActivity(SystemClock.uptimeMillis(), 8463 PowerManager.USER_ACTIVITY_EVENT_OTHER, 8464 0); 8465 8466 // start the confirmation countdown 8467 startConfirmationTimeout(token, params); 8468 8469 // wait for the backup to be performed 8470 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion..."); 8471 waitForCompletion(params); 8472 } finally { 8473 try { 8474 fd.close(); 8475 } catch (IOException e) { 8476 // just eat it 8477 } 8478 Binder.restoreCallingIdentity(oldId); 8479 Slog.d(TAG, "Full backup processing complete."); 8480 } 8481 } 8482 8483 public void fullTransportBackup(String[] pkgNames) { 8484 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, 8485 "fullTransportBackup"); 8486 8487 final int callingUserHandle = UserHandle.getCallingUserId(); 8488 if (callingUserHandle != UserHandle.USER_OWNER) { 8489 throw new IllegalStateException("Restore supported only for the device owner"); 8490 } 8491 8492 if (DEBUG) { 8493 Slog.d(TAG, "fullTransportBackup()"); 8494 } 8495 8496 CountDownLatch latch = new CountDownLatch(1); 8497 PerformFullTransportBackupTask task = 8498 new PerformFullTransportBackupTask(null, pkgNames, false, null, latch); 8499 (new Thread(task, "full-transport-master")).start(); 8500 do { 8501 try { 8502 latch.await(); 8503 break; 8504 } catch (InterruptedException e) { 8505 // Just go back to waiting for the latch to indicate completion 8506 } 8507 } while (true); 8508 8509 // We just ran a backup on these packages, so kick them to the end of the queue 8510 final long now = System.currentTimeMillis(); 8511 for (String pkg : pkgNames) { 8512 enqueueFullBackup(pkg, now); 8513 } 8514 8515 if (DEBUG) { 8516 Slog.d(TAG, "Done with full transport backup."); 8517 } 8518 } 8519 8520 public void fullRestore(ParcelFileDescriptor fd) { 8521 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); 8522 8523 final int callingUserHandle = UserHandle.getCallingUserId(); 8524 if (callingUserHandle != UserHandle.USER_OWNER) { 8525 throw new IllegalStateException("Restore supported only for the device owner"); 8526 } 8527 8528 long oldId = Binder.clearCallingIdentity(); 8529 8530 try { 8531 // Check whether the device has been provisioned -- we don't handle 8532 // full restores prior to completing the setup process. 8533 if (!deviceIsProvisioned()) { 8534 Slog.i(TAG, "Full restore not permitted before setup"); 8535 return; 8536 } 8537 8538 Slog.i(TAG, "Beginning full restore..."); 8539 8540 FullRestoreParams params = new FullRestoreParams(fd); 8541 final int token = generateToken(); 8542 synchronized (mFullConfirmations) { 8543 mFullConfirmations.put(token, params); 8544 } 8545 8546 // start up the confirmation UI 8547 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token); 8548 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) { 8549 Slog.e(TAG, "Unable to launch full restore confirmation"); 8550 mFullConfirmations.delete(token); 8551 return; 8552 } 8553 8554 // make sure the screen is lit for the user interaction 8555 mPowerManager.userActivity(SystemClock.uptimeMillis(), 8556 PowerManager.USER_ACTIVITY_EVENT_OTHER, 8557 0); 8558 8559 // start the confirmation countdown 8560 startConfirmationTimeout(token, params); 8561 8562 // wait for the restore to be performed 8563 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion..."); 8564 waitForCompletion(params); 8565 } finally { 8566 try { 8567 fd.close(); 8568 } catch (IOException e) { 8569 Slog.w(TAG, "Error trying to close fd after full restore: " + e); 8570 } 8571 Binder.restoreCallingIdentity(oldId); 8572 Slog.i(TAG, "Full restore processing complete."); 8573 } 8574 } 8575 8576 boolean startConfirmationUi(int token, String action) { 8577 try { 8578 Intent confIntent = new Intent(action); 8579 confIntent.setClassName("com.android.backupconfirm", 8580 "com.android.backupconfirm.BackupRestoreConfirmation"); 8581 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token); 8582 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 8583 mContext.startActivity(confIntent); 8584 } catch (ActivityNotFoundException e) { 8585 return false; 8586 } 8587 return true; 8588 } 8589 8590 void startConfirmationTimeout(int token, FullParams params) { 8591 if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after " 8592 + TIMEOUT_FULL_CONFIRMATION + " millis"); 8593 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, 8594 token, 0, params); 8595 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION); 8596 } 8597 8598 void waitForCompletion(FullParams params) { 8599 synchronized (params.latch) { 8600 while (params.latch.get() == false) { 8601 try { 8602 params.latch.wait(); 8603 } catch (InterruptedException e) { /* never interrupted */ } 8604 } 8605 } 8606 } 8607 8608 void signalFullBackupRestoreCompletion(FullParams params) { 8609 synchronized (params.latch) { 8610 params.latch.set(true); 8611 params.latch.notifyAll(); 8612 } 8613 } 8614 8615 // Confirm that the previously-requested full backup/restore operation can proceed. This 8616 // is used to require a user-facing disclosure about the operation. 8617 public void acknowledgeFullBackupOrRestore(int token, boolean allow, 8618 String curPassword, String encPpassword, IFullBackupRestoreObserver observer) { 8619 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token 8620 + " allow=" + allow); 8621 8622 // TODO: possibly require not just this signature-only permission, but even 8623 // require that the specific designated confirmation-UI app uid is the caller? 8624 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore"); 8625 8626 long oldId = Binder.clearCallingIdentity(); 8627 try { 8628 8629 FullParams params; 8630 synchronized (mFullConfirmations) { 8631 params = mFullConfirmations.get(token); 8632 if (params != null) { 8633 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params); 8634 mFullConfirmations.delete(token); 8635 8636 if (allow) { 8637 final int verb = params instanceof FullBackupParams 8638 ? MSG_RUN_ADB_BACKUP 8639 : MSG_RUN_ADB_RESTORE; 8640 8641 params.observer = observer; 8642 params.curPassword = curPassword; 8643 8644 params.encryptPassword = encPpassword; 8645 8646 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb); 8647 mWakelock.acquire(); 8648 Message msg = mBackupHandler.obtainMessage(verb, params); 8649 mBackupHandler.sendMessage(msg); 8650 } else { 8651 Slog.w(TAG, "User rejected full backup/restore operation"); 8652 // indicate completion without having actually transferred any data 8653 signalFullBackupRestoreCompletion(params); 8654 } 8655 } else { 8656 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token"); 8657 } 8658 } 8659 } finally { 8660 Binder.restoreCallingIdentity(oldId); 8661 } 8662 } 8663 8664 // Enable/disable backups 8665 public void setBackupEnabled(boolean enable) { 8666 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8667 "setBackupEnabled"); 8668 8669 Slog.i(TAG, "Backup enabled => " + enable); 8670 8671 long oldId = Binder.clearCallingIdentity(); 8672 try { 8673 boolean wasEnabled = mEnabled; 8674 synchronized (this) { 8675 Settings.Secure.putInt(mContext.getContentResolver(), 8676 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0); 8677 mEnabled = enable; 8678 } 8679 8680 synchronized (mQueueLock) { 8681 if (enable && !wasEnabled && mProvisioned) { 8682 // if we've just been enabled, start scheduling backup passes 8683 KeyValueBackupJob.schedule(mContext); 8684 scheduleNextFullBackupJob(0); 8685 } else if (!enable) { 8686 // No longer enabled, so stop running backups 8687 if (DEBUG) Slog.i(TAG, "Opting out of backup"); 8688 8689 KeyValueBackupJob.cancel(mContext); 8690 8691 // This also constitutes an opt-out, so we wipe any data for 8692 // this device from the backend. We start that process with 8693 // an alarm in order to guarantee wakelock states. 8694 if (wasEnabled && mProvisioned) { 8695 // NOTE: we currently flush every registered transport, not just 8696 // the currently-active one. 8697 HashSet<String> allTransports; 8698 synchronized (mTransports) { 8699 allTransports = new HashSet<String>(mTransports.keySet()); 8700 } 8701 // build the set of transports for which we are posting an init 8702 for (String transport : allTransports) { 8703 recordInitPendingLocked(true, transport); 8704 } 8705 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 8706 mRunInitIntent); 8707 } 8708 } 8709 } 8710 } finally { 8711 Binder.restoreCallingIdentity(oldId); 8712 } 8713 } 8714 8715 // Enable/disable automatic restore of app data at install time 8716 public void setAutoRestore(boolean doAutoRestore) { 8717 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8718 "setAutoRestore"); 8719 8720 Slog.i(TAG, "Auto restore => " + doAutoRestore); 8721 8722 final long oldId = Binder.clearCallingIdentity(); 8723 try { 8724 synchronized (this) { 8725 Settings.Secure.putInt(mContext.getContentResolver(), 8726 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0); 8727 mAutoRestore = doAutoRestore; 8728 } 8729 } finally { 8730 Binder.restoreCallingIdentity(oldId); 8731 } 8732 } 8733 8734 // Mark the backup service as having been provisioned 8735 public void setBackupProvisioned(boolean available) { 8736 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8737 "setBackupProvisioned"); 8738 /* 8739 * This is now a no-op; provisioning is simply the device's own setup state. 8740 */ 8741 } 8742 8743 // Report whether the backup mechanism is currently enabled 8744 public boolean isBackupEnabled() { 8745 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); 8746 return mEnabled; // no need to synchronize just to read it 8747 } 8748 8749 // Report the name of the currently active transport 8750 public String getCurrentTransport() { 8751 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8752 "getCurrentTransport"); 8753 if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); 8754 return mCurrentTransport; 8755 } 8756 8757 // Report all known, available backup transports 8758 public String[] listAllTransports() { 8759 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); 8760 8761 String[] list = null; 8762 ArrayList<String> known = new ArrayList<String>(); 8763 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { 8764 if (entry.getValue() != null) { 8765 known.add(entry.getKey()); 8766 } 8767 } 8768 8769 if (known.size() > 0) { 8770 list = new String[known.size()]; 8771 known.toArray(list); 8772 } 8773 return list; 8774 } 8775 8776 // Select which transport to use for the next backup operation. 8777 public String selectBackupTransport(String transport) { 8778 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8779 "selectBackupTransport"); 8780 8781 synchronized (mTransports) { 8782 final long oldId = Binder.clearCallingIdentity(); 8783 try { 8784 String prevTransport = mCurrentTransport; 8785 mCurrentTransport = transport; 8786 Settings.Secure.putString(mContext.getContentResolver(), 8787 Settings.Secure.BACKUP_TRANSPORT, transport); 8788 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport 8789 + " returning " + prevTransport); 8790 return prevTransport; 8791 } finally { 8792 Binder.restoreCallingIdentity(oldId); 8793 } 8794 } 8795 } 8796 8797 // Supply the configuration Intent for the given transport. If the name is not one 8798 // of the available transports, or if the transport does not supply any configuration 8799 // UI, the method returns null. 8800 public Intent getConfigurationIntent(String transportName) { 8801 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8802 "getConfigurationIntent"); 8803 8804 synchronized (mTransports) { 8805 final IBackupTransport transport = mTransports.get(transportName); 8806 if (transport != null) { 8807 try { 8808 final Intent intent = transport.configurationIntent(); 8809 if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent " 8810 + intent); 8811 return intent; 8812 } catch (RemoteException e) { 8813 /* fall through to return null */ 8814 } 8815 } 8816 } 8817 8818 return null; 8819 } 8820 8821 // Supply the configuration summary string for the given transport. If the name is 8822 // not one of the available transports, or if the transport does not supply any 8823 // summary / destination string, the method can return null. 8824 // 8825 // This string is used VERBATIM as the summary text of the relevant Settings item! 8826 public String getDestinationString(String transportName) { 8827 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8828 "getDestinationString"); 8829 8830 synchronized (mTransports) { 8831 final IBackupTransport transport = mTransports.get(transportName); 8832 if (transport != null) { 8833 try { 8834 final String text = transport.currentDestinationString(); 8835 if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text); 8836 return text; 8837 } catch (RemoteException e) { 8838 /* fall through to return null */ 8839 } 8840 } 8841 } 8842 8843 return null; 8844 } 8845 8846 // Supply the manage-data intent for the given transport. 8847 public Intent getDataManagementIntent(String transportName) { 8848 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8849 "getDataManagementIntent"); 8850 8851 synchronized (mTransports) { 8852 final IBackupTransport transport = mTransports.get(transportName); 8853 if (transport != null) { 8854 try { 8855 final Intent intent = transport.dataManagementIntent(); 8856 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent " 8857 + intent); 8858 return intent; 8859 } catch (RemoteException e) { 8860 /* fall through to return null */ 8861 } 8862 } 8863 } 8864 8865 return null; 8866 } 8867 8868 // Supply the menu label for affordances that fire the manage-data intent 8869 // for the given transport. 8870 public String getDataManagementLabel(String transportName) { 8871 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8872 "getDataManagementLabel"); 8873 8874 synchronized (mTransports) { 8875 final IBackupTransport transport = mTransports.get(transportName); 8876 if (transport != null) { 8877 try { 8878 final String text = transport.dataManagementLabel(); 8879 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() 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 // Callback: a requested backup agent has been instantiated. This should only 8891 // be called from the Activity Manager. 8892 public void agentConnected(String packageName, IBinder agentBinder) { 8893 synchronized(mAgentConnectLock) { 8894 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 8895 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); 8896 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); 8897 mConnectedAgent = agent; 8898 mConnecting = false; 8899 } else { 8900 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 8901 + " claiming agent connected"); 8902 } 8903 mAgentConnectLock.notifyAll(); 8904 } 8905 } 8906 8907 // Callback: a backup agent has failed to come up, or has unexpectedly quit. 8908 // If the agent failed to come up in the first place, the agentBinder argument 8909 // will be null. This should only be called from the Activity Manager. 8910 public void agentDisconnected(String packageName) { 8911 // TODO: handle backup being interrupted 8912 synchronized(mAgentConnectLock) { 8913 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 8914 mConnectedAgent = null; 8915 mConnecting = false; 8916 } else { 8917 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 8918 + " claiming agent disconnected"); 8919 } 8920 mAgentConnectLock.notifyAll(); 8921 } 8922 } 8923 8924 // An application being installed will need a restore pass, then the Package Manager 8925 // will need to be told when the restore is finished. 8926 public void restoreAtInstall(String packageName, int token) { 8927 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 8928 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 8929 + " attemping install-time restore"); 8930 return; 8931 } 8932 8933 boolean skip = false; 8934 8935 long restoreSet = getAvailableRestoreToken(packageName); 8936 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName 8937 + " token=" + Integer.toHexString(token) 8938 + " restoreSet=" + Long.toHexString(restoreSet)); 8939 if (restoreSet == 0) { 8940 if (MORE_DEBUG) Slog.i(TAG, "No restore set"); 8941 skip = true; 8942 } 8943 8944 // Do we have a transport to fetch data for us? 8945 IBackupTransport transport = getTransport(mCurrentTransport); 8946 if (transport == null) { 8947 if (DEBUG) Slog.w(TAG, "No transport"); 8948 skip = true; 8949 } 8950 8951 if (!mAutoRestore) { 8952 if (DEBUG) { 8953 Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore); 8954 } 8955 skip = true; 8956 } 8957 8958 if (!skip) { 8959 try { 8960 // okay, we're going to attempt a restore of this package from this restore set. 8961 // The eventual message back into the Package Manager to run the post-install 8962 // steps for 'token' will be issued from the restore handling code. 8963 8964 // This can throw and so *must* happen before the wakelock is acquired 8965 String dirName = transport.transportDirName(); 8966 8967 // We can use a synthetic PackageInfo here because: 8968 // 1. We know it's valid, since the Package Manager supplied the name 8969 // 2. Only the packageName field will be used by the restore code 8970 PackageInfo pkg = new PackageInfo(); 8971 pkg.packageName = packageName; 8972 8973 mWakelock.acquire(); 8974 if (MORE_DEBUG) { 8975 Slog.d(TAG, "Restore at install of " + packageName); 8976 } 8977 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 8978 msg.obj = new RestoreParams(transport, dirName, null, 8979 restoreSet, pkg, token); 8980 mBackupHandler.sendMessage(msg); 8981 } catch (RemoteException e) { 8982 // Binding to the transport broke; back off and proceed with the installation. 8983 Slog.e(TAG, "Unable to contact transport"); 8984 skip = true; 8985 } 8986 } 8987 8988 if (skip) { 8989 // Auto-restore disabled or no way to attempt a restore; just tell the Package 8990 // Manager to proceed with the post-install handling for this package. 8991 if (DEBUG) Slog.v(TAG, "Finishing install immediately"); 8992 try { 8993 mPackageManagerBinder.finishPackageInstall(token); 8994 } catch (RemoteException e) { /* can't happen */ } 8995 } 8996 } 8997 8998 // Hand off a restore session 8999 public IRestoreSession beginRestoreSession(String packageName, String transport) { 9000 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName 9001 + " transport=" + transport); 9002 9003 boolean needPermission = true; 9004 if (transport == null) { 9005 transport = mCurrentTransport; 9006 9007 if (packageName != null) { 9008 PackageInfo app = null; 9009 try { 9010 app = mPackageManager.getPackageInfo(packageName, 0); 9011 } catch (NameNotFoundException nnf) { 9012 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 9013 throw new IllegalArgumentException("Package " + packageName + " not found"); 9014 } 9015 9016 if (app.applicationInfo.uid == Binder.getCallingUid()) { 9017 // So: using the current active transport, and the caller has asked 9018 // that its own package will be restored. In this narrow use case 9019 // we do not require the caller to hold the permission. 9020 needPermission = false; 9021 } 9022 } 9023 } 9024 9025 if (needPermission) { 9026 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9027 "beginRestoreSession"); 9028 } else { 9029 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed"); 9030 } 9031 9032 synchronized(this) { 9033 if (mActiveRestoreSession != null) { 9034 Slog.d(TAG, "Restore session requested but one already active"); 9035 return null; 9036 } 9037 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport); 9038 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 9039 } 9040 return mActiveRestoreSession; 9041 } 9042 9043 void clearRestoreSession(ActiveRestoreSession currentSession) { 9044 synchronized(this) { 9045 if (currentSession != mActiveRestoreSession) { 9046 Slog.e(TAG, "ending non-current restore session"); 9047 } else { 9048 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout"); 9049 mActiveRestoreSession = null; 9050 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9051 } 9052 } 9053 } 9054 9055 // Note that a currently-active backup agent has notified us that it has 9056 // completed the given outstanding asynchronous backup/restore operation. 9057 public void opComplete(int token, long result) { 9058 if (MORE_DEBUG) { 9059 Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result); 9060 } 9061 Operation op = null; 9062 synchronized (mCurrentOpLock) { 9063 op = mCurrentOperations.get(token); 9064 if (op != null) { 9065 op.state = OP_ACKNOWLEDGED; 9066 } 9067 mCurrentOpLock.notifyAll(); 9068 } 9069 9070 // The completion callback, if any, is invoked on the handler 9071 if (op != null && op.callback != null) { 9072 Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback); 9073 // NB: this cannot distinguish between results > 2 gig 9074 msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result; 9075 mBackupHandler.sendMessage(msg); 9076 } 9077 } 9078 9079 // ----- Restore session ----- 9080 9081 class ActiveRestoreSession extends IRestoreSession.Stub { 9082 private static final String TAG = "RestoreSession"; 9083 9084 private String mPackageName; 9085 private IBackupTransport mRestoreTransport = null; 9086 RestoreSet[] mRestoreSets = null; 9087 boolean mEnded = false; 9088 boolean mTimedOut = false; 9089 9090 ActiveRestoreSession(String packageName, String transport) { 9091 mPackageName = packageName; 9092 mRestoreTransport = getTransport(transport); 9093 } 9094 9095 public void markTimedOut() { 9096 mTimedOut = true; 9097 } 9098 9099 // --- Binder interface --- 9100 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) { 9101 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9102 "getAvailableRestoreSets"); 9103 if (observer == null) { 9104 throw new IllegalArgumentException("Observer must not be null"); 9105 } 9106 9107 if (mEnded) { 9108 throw new IllegalStateException("Restore session already ended"); 9109 } 9110 9111 if (mTimedOut) { 9112 Slog.i(TAG, "Session already timed out"); 9113 return -1; 9114 } 9115 9116 long oldId = Binder.clearCallingIdentity(); 9117 try { 9118 if (mRestoreTransport == null) { 9119 Slog.w(TAG, "Null transport getting restore sets"); 9120 return -1; 9121 } 9122 9123 // We know we're doing legit work now, so halt the timeout 9124 // until we're done. It gets started again when the result 9125 // comes in. 9126 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9127 9128 // spin off the transport request to our service thread 9129 mWakelock.acquire(); 9130 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS, 9131 new RestoreGetSetsParams(mRestoreTransport, this, observer)); 9132 mBackupHandler.sendMessage(msg); 9133 return 0; 9134 } catch (Exception e) { 9135 Slog.e(TAG, "Error in getAvailableRestoreSets", e); 9136 return -1; 9137 } finally { 9138 Binder.restoreCallingIdentity(oldId); 9139 } 9140 } 9141 9142 public synchronized int restoreAll(long token, IRestoreObserver observer) { 9143 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9144 "performRestore"); 9145 9146 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token) 9147 + " observer=" + observer); 9148 9149 if (mEnded) { 9150 throw new IllegalStateException("Restore session already ended"); 9151 } 9152 9153 if (mTimedOut) { 9154 Slog.i(TAG, "Session already timed out"); 9155 return -1; 9156 } 9157 9158 if (mRestoreTransport == null || mRestoreSets == null) { 9159 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 9160 return -1; 9161 } 9162 9163 if (mPackageName != null) { 9164 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 9165 return -1; 9166 } 9167 9168 String dirName; 9169 try { 9170 dirName = mRestoreTransport.transportDirName(); 9171 } catch (RemoteException e) { 9172 // Transport went AWOL; fail. 9173 Slog.e(TAG, "Unable to contact transport for restore"); 9174 return -1; 9175 } 9176 9177 synchronized (mQueueLock) { 9178 for (int i = 0; i < mRestoreSets.length; i++) { 9179 if (token == mRestoreSets[i].token) { 9180 // Real work, so stop the session timeout until we finalize the restore 9181 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9182 9183 long oldId = Binder.clearCallingIdentity(); 9184 mWakelock.acquire(); 9185 if (MORE_DEBUG) { 9186 Slog.d(TAG, "restoreAll() kicking off"); 9187 } 9188 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9189 msg.obj = new RestoreParams(mRestoreTransport, dirName, 9190 observer, token); 9191 mBackupHandler.sendMessage(msg); 9192 Binder.restoreCallingIdentity(oldId); 9193 return 0; 9194 } 9195 } 9196 } 9197 9198 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 9199 return -1; 9200 } 9201 9202 // Restores of more than a single package are treated as 'system' restores 9203 public synchronized int restoreSome(long token, IRestoreObserver observer, 9204 String[] packages) { 9205 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9206 "performRestore"); 9207 9208 if (DEBUG) { 9209 StringBuilder b = new StringBuilder(128); 9210 b.append("restoreSome token="); 9211 b.append(Long.toHexString(token)); 9212 b.append(" observer="); 9213 b.append(observer.toString()); 9214 b.append(" packages="); 9215 if (packages == null) { 9216 b.append("null"); 9217 } else { 9218 b.append('{'); 9219 boolean first = true; 9220 for (String s : packages) { 9221 if (!first) { 9222 b.append(", "); 9223 } else first = false; 9224 b.append(s); 9225 } 9226 b.append('}'); 9227 } 9228 Slog.d(TAG, b.toString()); 9229 } 9230 9231 if (mEnded) { 9232 throw new IllegalStateException("Restore session already ended"); 9233 } 9234 9235 if (mTimedOut) { 9236 Slog.i(TAG, "Session already timed out"); 9237 return -1; 9238 } 9239 9240 if (mRestoreTransport == null || mRestoreSets == null) { 9241 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 9242 return -1; 9243 } 9244 9245 if (mPackageName != null) { 9246 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 9247 return -1; 9248 } 9249 9250 String dirName; 9251 try { 9252 dirName = mRestoreTransport.transportDirName(); 9253 } catch (RemoteException e) { 9254 // Transport went AWOL; fail. 9255 Slog.e(TAG, "Unable to contact transport for restore"); 9256 return -1; 9257 } 9258 9259 synchronized (mQueueLock) { 9260 for (int i = 0; i < mRestoreSets.length; i++) { 9261 if (token == mRestoreSets[i].token) { 9262 // Stop the session timeout until we finalize the restore 9263 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9264 9265 long oldId = Binder.clearCallingIdentity(); 9266 mWakelock.acquire(); 9267 if (MORE_DEBUG) { 9268 Slog.d(TAG, "restoreSome() of " + packages.length + " packages"); 9269 } 9270 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9271 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, 9272 packages, packages.length > 1); 9273 mBackupHandler.sendMessage(msg); 9274 Binder.restoreCallingIdentity(oldId); 9275 return 0; 9276 } 9277 } 9278 } 9279 9280 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 9281 return -1; 9282 } 9283 9284 public synchronized int restorePackage(String packageName, IRestoreObserver observer) { 9285 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); 9286 9287 if (mEnded) { 9288 throw new IllegalStateException("Restore session already ended"); 9289 } 9290 9291 if (mTimedOut) { 9292 Slog.i(TAG, "Session already timed out"); 9293 return -1; 9294 } 9295 9296 if (mPackageName != null) { 9297 if (! mPackageName.equals(packageName)) { 9298 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName 9299 + " on session for package " + mPackageName); 9300 return -1; 9301 } 9302 } 9303 9304 PackageInfo app = null; 9305 try { 9306 app = mPackageManager.getPackageInfo(packageName, 0); 9307 } catch (NameNotFoundException nnf) { 9308 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 9309 return -1; 9310 } 9311 9312 // If the caller is not privileged and is not coming from the target 9313 // app's uid, throw a permission exception back to the caller. 9314 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP, 9315 Binder.getCallingPid(), Binder.getCallingUid()); 9316 if ((perm == PackageManager.PERMISSION_DENIED) && 9317 (app.applicationInfo.uid != Binder.getCallingUid())) { 9318 Slog.w(TAG, "restorePackage: bad packageName=" + packageName 9319 + " or calling uid=" + Binder.getCallingUid()); 9320 throw new SecurityException("No permission to restore other packages"); 9321 } 9322 9323 // So far so good; we're allowed to try to restore this package. Now 9324 // check whether there is data for it in the current dataset, falling back 9325 // to the ancestral dataset if not. 9326 long token = getAvailableRestoreToken(packageName); 9327 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName 9328 + " token=" + Long.toHexString(token)); 9329 9330 // If we didn't come up with a place to look -- no ancestral dataset and 9331 // the app has never been backed up from this device -- there's nothing 9332 // to do but return failure. 9333 if (token == 0) { 9334 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring"); 9335 return -1; 9336 } 9337 9338 String dirName; 9339 try { 9340 dirName = mRestoreTransport.transportDirName(); 9341 } catch (RemoteException e) { 9342 // Transport went AWOL; fail. 9343 Slog.e(TAG, "Unable to contact transport for restore"); 9344 return -1; 9345 } 9346 9347 // Stop the session timeout until we finalize the restore 9348 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9349 9350 // Ready to go: enqueue the restore request and claim success 9351 long oldId = Binder.clearCallingIdentity(); 9352 mWakelock.acquire(); 9353 if (MORE_DEBUG) { 9354 Slog.d(TAG, "restorePackage() : " + packageName); 9355 } 9356 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9357 msg.obj = new RestoreParams(mRestoreTransport, dirName, 9358 observer, token, app, 0); 9359 mBackupHandler.sendMessage(msg); 9360 Binder.restoreCallingIdentity(oldId); 9361 return 0; 9362 } 9363 9364 // Posted to the handler to tear down a restore session in a cleanly synchronized way 9365 class EndRestoreRunnable implements Runnable { 9366 BackupManagerService mBackupManager; 9367 ActiveRestoreSession mSession; 9368 9369 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) { 9370 mBackupManager = manager; 9371 mSession = session; 9372 } 9373 9374 public void run() { 9375 // clean up the session's bookkeeping 9376 synchronized (mSession) { 9377 try { 9378 if (mSession.mRestoreTransport != null) { 9379 mSession.mRestoreTransport.finishRestore(); 9380 } 9381 } catch (Exception e) { 9382 Slog.e(TAG, "Error in finishRestore", e); 9383 } finally { 9384 mSession.mRestoreTransport = null; 9385 mSession.mEnded = true; 9386 } 9387 } 9388 9389 // clean up the BackupManagerImpl side of the bookkeeping 9390 // and cancel any pending timeout message 9391 mBackupManager.clearRestoreSession(mSession); 9392 } 9393 } 9394 9395 public synchronized void endRestoreSession() { 9396 if (DEBUG) Slog.d(TAG, "endRestoreSession"); 9397 9398 if (mTimedOut) { 9399 Slog.i(TAG, "Session already timed out"); 9400 return; 9401 } 9402 9403 if (mEnded) { 9404 throw new IllegalStateException("Restore session already ended"); 9405 } 9406 9407 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this)); 9408 } 9409 } 9410 9411 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 9412 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 9413 9414 long identityToken = Binder.clearCallingIdentity(); 9415 try { 9416 if (args != null) { 9417 for (String arg : args) { 9418 if ("-h".equals(arg)) { 9419 pw.println("'dumpsys backup' optional arguments:"); 9420 pw.println(" -h : this help text"); 9421 pw.println(" a[gents] : dump information about defined backup agents"); 9422 return; 9423 } else if ("agents".startsWith(arg)) { 9424 dumpAgents(pw); 9425 return; 9426 } 9427 } 9428 } 9429 dumpInternal(pw); 9430 } finally { 9431 Binder.restoreCallingIdentity(identityToken); 9432 } 9433 } 9434 9435 private void dumpAgents(PrintWriter pw) { 9436 List<PackageInfo> agentPackages = allAgentPackages(); 9437 pw.println("Defined backup agents:"); 9438 for (PackageInfo pkg : agentPackages) { 9439 pw.print(" "); 9440 pw.print(pkg.packageName); pw.println(':'); 9441 pw.print(" "); pw.println(pkg.applicationInfo.backupAgentName); 9442 } 9443 } 9444 9445 private void dumpInternal(PrintWriter pw) { 9446 synchronized (mQueueLock) { 9447 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") 9448 + " / " + (!mProvisioned ? "not " : "") + "provisioned / " 9449 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); 9450 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); 9451 if (mBackupRunning) pw.println("Backup currently running"); 9452 pw.println("Last backup pass started: " + mLastBackupPass 9453 + " (now = " + System.currentTimeMillis() + ')'); 9454 pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled()); 9455 9456 pw.println("Available transports:"); 9457 final String[] transports = listAllTransports(); 9458 if (transports != null) { 9459 for (String t : listAllTransports()) { 9460 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t); 9461 try { 9462 IBackupTransport transport = getTransport(t); 9463 File dir = new File(mBaseStateDir, transport.transportDirName()); 9464 pw.println(" destination: " + transport.currentDestinationString()); 9465 pw.println(" intent: " + transport.configurationIntent()); 9466 for (File f : dir.listFiles()) { 9467 pw.println(" " + f.getName() + " - " + f.length() + " state bytes"); 9468 } 9469 } catch (Exception e) { 9470 Slog.e(TAG, "Error in transport", e); 9471 pw.println(" Error: " + e); 9472 } 9473 } 9474 } 9475 9476 pw.println("Pending init: " + mPendingInits.size()); 9477 for (String s : mPendingInits) { 9478 pw.println(" " + s); 9479 } 9480 9481 if (DEBUG_BACKUP_TRACE) { 9482 synchronized (mBackupTrace) { 9483 if (!mBackupTrace.isEmpty()) { 9484 pw.println("Most recent backup trace:"); 9485 for (String s : mBackupTrace) { 9486 pw.println(" " + s); 9487 } 9488 } 9489 } 9490 } 9491 9492 int N = mBackupParticipants.size(); 9493 pw.println("Participants:"); 9494 for (int i=0; i<N; i++) { 9495 int uid = mBackupParticipants.keyAt(i); 9496 pw.print(" uid: "); 9497 pw.println(uid); 9498 HashSet<String> participants = mBackupParticipants.valueAt(i); 9499 for (String app: participants) { 9500 pw.println(" " + app); 9501 } 9502 } 9503 9504 pw.println("Ancestral packages: " 9505 + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); 9506 if (mAncestralPackages != null) { 9507 for (String pkg : mAncestralPackages) { 9508 pw.println(" " + pkg); 9509 } 9510 } 9511 9512 pw.println("Ever backed up: " + mEverStoredApps.size()); 9513 for (String pkg : mEverStoredApps) { 9514 pw.println(" " + pkg); 9515 } 9516 9517 pw.println("Pending key/value backup: " + mPendingBackups.size()); 9518 for (BackupRequest req : mPendingBackups.values()) { 9519 pw.println(" " + req); 9520 } 9521 9522 pw.println("Full backup queue:" + mFullBackupQueue.size()); 9523 for (FullBackupEntry entry : mFullBackupQueue) { 9524 pw.print(" "); pw.print(entry.lastBackup); 9525 pw.print(" : "); pw.println(entry.packageName); 9526 } 9527 } 9528 } 9529} 9530