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