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