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