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 17 package com.android.server.backup; 18 19 import android.app.ActivityManagerNative; 20 import android.app.AlarmManager; 21 import android.app.AppGlobals; 22 import android.app.IActivityManager; 23 import android.app.IApplicationThread; 24 import android.app.IBackupAgent; 25 import android.app.PendingIntent; 26 import android.app.backup.BackupAgent; 27 import android.app.backup.BackupDataInput; 28 import android.app.backup.BackupDataOutput; 29 import android.app.backup.BackupTransport; 30 import android.app.backup.FullBackup; 31 import android.app.backup.RestoreDescription; 32 import android.app.backup.RestoreSet; 33 import android.app.backup.IBackupManager; 34 import android.app.backup.IFullBackupRestoreObserver; 35 import android.app.backup.IRestoreObserver; 36 import android.app.backup.IRestoreSession; 37 import android.content.ActivityNotFoundException; 38 import android.content.BroadcastReceiver; 39 import android.content.ComponentName; 40 import android.content.ContentResolver; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.IntentFilter; 44 import android.content.ServiceConnection; 45 import android.content.pm.ApplicationInfo; 46 import android.content.pm.IPackageDataObserver; 47 import android.content.pm.IPackageDeleteObserver; 48 import android.content.pm.IPackageInstallObserver; 49 import android.content.pm.IPackageManager; 50 import android.content.pm.PackageInfo; 51 import android.content.pm.PackageManager; 52 import android.content.pm.ResolveInfo; 53 import android.content.pm.ServiceInfo; 54 import android.content.pm.Signature; 55 import android.content.pm.PackageManager.NameNotFoundException; 56 import android.database.ContentObserver; 57 import android.net.Uri; 58 import android.os.Binder; 59 import android.os.Build; 60 import android.os.Bundle; 61 import android.os.Environment; 62 import android.os.Handler; 63 import android.os.HandlerThread; 64 import android.os.IBinder; 65 import android.os.Looper; 66 import android.os.Message; 67 import android.os.ParcelFileDescriptor; 68 import android.os.PowerManager; 69 import android.os.Process; 70 import android.os.RemoteException; 71 import android.os.SELinux; 72 import android.os.ServiceManager; 73 import android.os.SystemClock; 74 import android.os.UserHandle; 75 import android.os.WorkSource; 76 import android.os.Environment.UserEnvironment; 77 import android.os.storage.IMountService; 78 import android.os.storage.StorageManager; 79 import android.provider.Settings; 80 import android.system.ErrnoException; 81 import android.system.Os; 82 import android.util.ArrayMap; 83 import android.util.AtomicFile; 84 import android.util.EventLog; 85 import android.util.Log; 86 import android.util.Slog; 87 import android.util.SparseArray; 88 import android.util.StringBuilderPrinter; 89 90 import com.android.internal.backup.IBackupTransport; 91 import com.android.internal.backup.IObbBackupService; 92 import com.android.server.AppWidgetBackupBridge; 93 import com.android.server.EventLogTags; 94 import com.android.server.SystemService; 95 import com.android.server.backup.PackageManagerBackupAgent.Metadata; 96 97 import java.io.BufferedInputStream; 98 import java.io.BufferedOutputStream; 99 import java.io.ByteArrayInputStream; 100 import java.io.ByteArrayOutputStream; 101 import java.io.DataInputStream; 102 import java.io.DataOutputStream; 103 import java.io.EOFException; 104 import java.io.File; 105 import java.io.FileDescriptor; 106 import java.io.FileInputStream; 107 import java.io.FileNotFoundException; 108 import java.io.FileOutputStream; 109 import java.io.IOException; 110 import java.io.InputStream; 111 import java.io.OutputStream; 112 import java.io.PrintWriter; 113 import java.io.RandomAccessFile; 114 import java.security.InvalidAlgorithmParameterException; 115 import java.security.InvalidKeyException; 116 import java.security.Key; 117 import java.security.MessageDigest; 118 import java.security.NoSuchAlgorithmException; 119 import java.security.SecureRandom; 120 import java.security.spec.InvalidKeySpecException; 121 import java.security.spec.KeySpec; 122 import java.text.SimpleDateFormat; 123 import java.util.ArrayList; 124 import java.util.Arrays; 125 import java.util.Collections; 126 import java.util.Date; 127 import java.util.HashMap; 128 import java.util.HashSet; 129 import java.util.Iterator; 130 import java.util.List; 131 import java.util.Map; 132 import java.util.Map.Entry; 133 import java.util.Objects; 134 import java.util.Random; 135 import java.util.Set; 136 import java.util.TreeMap; 137 import java.util.concurrent.atomic.AtomicBoolean; 138 import java.util.concurrent.atomic.AtomicInteger; 139 import java.util.zip.Deflater; 140 import java.util.zip.DeflaterOutputStream; 141 import java.util.zip.InflaterInputStream; 142 143 import javax.crypto.BadPaddingException; 144 import javax.crypto.Cipher; 145 import javax.crypto.CipherInputStream; 146 import javax.crypto.CipherOutputStream; 147 import javax.crypto.IllegalBlockSizeException; 148 import javax.crypto.NoSuchPaddingException; 149 import javax.crypto.SecretKey; 150 import javax.crypto.SecretKeyFactory; 151 import javax.crypto.spec.IvParameterSpec; 152 import javax.crypto.spec.PBEKeySpec; 153 import javax.crypto.spec.SecretKeySpec; 154 155 import libcore.io.IoUtils; 156 157 public class BackupManagerService { 158 159 private static final String TAG = "BackupManagerService"; 160 private static final boolean DEBUG = true; 161 private static final boolean MORE_DEBUG = false; 162 private 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 // How often we perform a backup pass. Privileged external callers can 199 // trigger an immediate pass. 200 private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR; 201 202 // Random variation in backup scheduling time to avoid server load spikes 203 private static final int FUZZ_MILLIS = 5 * 60 * 1000; 204 205 // The amount of time between the initial provisioning of the device and 206 // the first backup pass. 207 private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR; 208 209 // Retry interval for clear/init when the transport is unavailable 210 private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR; 211 212 private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN"; 213 private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT"; 214 private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR"; 215 private static final int MSG_RUN_BACKUP = 1; 216 private static final int MSG_RUN_ADB_BACKUP = 2; 217 private static final int MSG_RUN_RESTORE = 3; 218 private static final int MSG_RUN_CLEAR = 4; 219 private static final int MSG_RUN_INITIALIZE = 5; 220 private static final int MSG_RUN_GET_RESTORE_SETS = 6; 221 private static final int MSG_TIMEOUT = 7; 222 private static final int MSG_RESTORE_TIMEOUT = 8; 223 private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9; 224 private static final int MSG_RUN_ADB_RESTORE = 10; 225 private static final int MSG_RETRY_INIT = 11; 226 private static final int MSG_RETRY_CLEAR = 12; 227 private static final int MSG_WIDGET_BROADCAST = 13; 228 private static final int MSG_RUN_FULL_TRANSPORT_BACKUP = 14; 229 230 // backup task state machine tick 231 static final int MSG_BACKUP_RESTORE_STEP = 20; 232 static final int MSG_OP_COMPLETE = 21; 233 234 // Timeout interval for deciding that a bind or clear-data has taken too long 235 static final long TIMEOUT_INTERVAL = 10 * 1000; 236 237 // Timeout intervals for agent backup & restore operations 238 static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000; 239 static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000; 240 static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000; 241 static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000; 242 static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000; 243 244 // User confirmation timeout for a full backup/restore operation. It's this long in 245 // order to give them time to enter the backup password. 246 static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000; 247 248 // How long between attempts to perform a full-data backup of any given app 249 static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day 250 251 Context mContext; 252 private PackageManager mPackageManager; 253 IPackageManager mPackageManagerBinder; 254 private IActivityManager mActivityManager; 255 private PowerManager mPowerManager; 256 private AlarmManager mAlarmManager; 257 private IMountService mMountService; 258 IBackupManager mBackupManagerBinder; 259 260 boolean mEnabled; // access to this is synchronized on 'this' 261 boolean mProvisioned; 262 boolean mAutoRestore; 263 PowerManager.WakeLock mWakelock; 264 HandlerThread mHandlerThread; 265 BackupHandler mBackupHandler; 266 PendingIntent mRunBackupIntent, mRunInitIntent; 267 BroadcastReceiver mRunBackupReceiver, mRunInitReceiver; 268 // map UIDs to the set of participating packages under that UID 269 final SparseArray<HashSet<String>> mBackupParticipants 270 = new SparseArray<HashSet<String>>(); 271 // set of backup services that have pending changes 272 class BackupRequest { 273 public String packageName; 274 275 BackupRequest(String pkgName) { 276 packageName = pkgName; 277 } 278 279 public String toString() { 280 return "BackupRequest{pkg=" + packageName + "}"; 281 } 282 } 283 // Backups that we haven't started yet. Keys are package names. 284 HashMap<String,BackupRequest> mPendingBackups 285 = new HashMap<String,BackupRequest>(); 286 287 // Pseudoname that we use for the Package Manager metadata "package" 288 static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; 289 290 // locking around the pending-backup management 291 final Object mQueueLock = new Object(); 292 293 // The thread performing the sequence of queued backups binds to each app's agent 294 // in succession. Bind notifications are asynchronously delivered through the 295 // Activity Manager; use this lock object to signal when a requested binding has 296 // completed. 297 final Object mAgentConnectLock = new Object(); 298 IBackupAgent mConnectedAgent; 299 volatile boolean mBackupRunning; 300 volatile boolean mConnecting; 301 volatile long mLastBackupPass; 302 volatile long mNextBackupPass; 303 304 // For debugging, we maintain a progress trace of operations during backup 305 static final boolean DEBUG_BACKUP_TRACE = true; 306 final List<String> mBackupTrace = new ArrayList<String>(); 307 308 // A similar synchronization mechanism around clearing apps' data for restore 309 final Object mClearDataLock = new Object(); 310 volatile boolean mClearingData; 311 312 // Transport bookkeeping 313 final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST); 314 final ArrayMap<String,String> mTransportNames 315 = new ArrayMap<String,String>(); // component name -> registration name 316 final ArrayMap<String,IBackupTransport> mTransports 317 = new ArrayMap<String,IBackupTransport>(); // registration name -> binder 318 final ArrayMap<String,TransportConnection> mTransportConnections 319 = new ArrayMap<String,TransportConnection>(); 320 String mCurrentTransport; 321 ActiveRestoreSession mActiveRestoreSession; 322 323 // Watch the device provisioning operation during setup 324 ContentObserver mProvisionedObserver; 325 326 // The published binder is actually to a singleton trampoline object that calls 327 // through to the proper code. This indirection lets us turn down the heavy 328 // implementation object on the fly without disturbing binders that have been 329 // cached elsewhere in the system. 330 static Trampoline sInstance; 331 static Trampoline getInstance() { 332 // Always constructed during system bringup, so no need to lazy-init 333 return sInstance; 334 } 335 336 public static final class Lifecycle extends SystemService { 337 338 public Lifecycle(Context context) { 339 super(context); 340 sInstance = new Trampoline(context); 341 } 342 343 @Override 344 public void onStart() { 345 publishBinderService(Context.BACKUP_SERVICE, sInstance); 346 } 347 348 @Override 349 public void onBootPhase(int phase) { 350 if (phase == PHASE_SYSTEM_SERVICES_READY) { 351 sInstance.initialize(UserHandle.USER_OWNER); 352 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 353 ContentResolver r = sInstance.mContext.getContentResolver(); 354 boolean areEnabled = Settings.Secure.getInt(r, 355 Settings.Secure.BACKUP_ENABLED, 0) != 0; 356 try { 357 sInstance.setBackupEnabled(areEnabled); 358 } catch (RemoteException e) { 359 // can't happen; it's a local object 360 } 361 } 362 } 363 } 364 365 class ProvisionedObserver extends ContentObserver { 366 public ProvisionedObserver(Handler handler) { 367 super(handler); 368 } 369 370 public void onChange(boolean selfChange) { 371 final boolean wasProvisioned = mProvisioned; 372 final boolean isProvisioned = deviceIsProvisioned(); 373 // latch: never unprovision 374 mProvisioned = wasProvisioned || isProvisioned; 375 if (MORE_DEBUG) { 376 Slog.d(TAG, "Provisioning change: was=" + wasProvisioned 377 + " is=" + isProvisioned + " now=" + mProvisioned); 378 } 379 380 synchronized (mQueueLock) { 381 if (mProvisioned && !wasProvisioned && mEnabled) { 382 // we're now good to go, so start the backup alarms 383 if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups"); 384 startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL); 385 scheduleNextFullBackupJob(); 386 } 387 } 388 } 389 } 390 391 class RestoreGetSetsParams { 392 public IBackupTransport transport; 393 public ActiveRestoreSession session; 394 public IRestoreObserver observer; 395 396 RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session, 397 IRestoreObserver _observer) { 398 transport = _transport; 399 session = _session; 400 observer = _observer; 401 } 402 } 403 404 class RestoreParams { 405 public IBackupTransport transport; 406 public String dirName; 407 public IRestoreObserver observer; 408 public long token; 409 public PackageInfo pkgInfo; 410 public int pmToken; // in post-install restore, the PM's token for this transaction 411 public boolean isSystemRestore; 412 public String[] filterSet; 413 414 // Restore a single package 415 RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, 416 long _token, PackageInfo _pkg, int _pmToken) { 417 transport = _transport; 418 dirName = _dirName; 419 observer = _obs; 420 token = _token; 421 pkgInfo = _pkg; 422 pmToken = _pmToken; 423 isSystemRestore = false; 424 filterSet = null; 425 } 426 427 // Restore everything possible. This is the form that Setup Wizard or similar 428 // restore UXes use. 429 RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, 430 long _token) { 431 transport = _transport; 432 dirName = _dirName; 433 observer = _obs; 434 token = _token; 435 pkgInfo = null; 436 pmToken = 0; 437 isSystemRestore = true; 438 filterSet = null; 439 } 440 441 // Restore some set of packages. Leave this one up to the caller to specify 442 // whether it's to be considered a system-level restore. 443 RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, 444 long _token, String[] _filterSet, boolean _isSystemRestore) { 445 transport = _transport; 446 dirName = _dirName; 447 observer = _obs; 448 token = _token; 449 pkgInfo = null; 450 pmToken = 0; 451 isSystemRestore = _isSystemRestore; 452 filterSet = _filterSet; 453 } 454 } 455 456 class ClearParams { 457 public IBackupTransport transport; 458 public PackageInfo packageInfo; 459 460 ClearParams(IBackupTransport _transport, PackageInfo _info) { 461 transport = _transport; 462 packageInfo = _info; 463 } 464 } 465 466 class ClearRetryParams { 467 public String transportName; 468 public String packageName; 469 470 ClearRetryParams(String transport, String pkg) { 471 transportName = transport; 472 packageName = pkg; 473 } 474 } 475 476 class FullParams { 477 public ParcelFileDescriptor fd; 478 public final AtomicBoolean latch; 479 public IFullBackupRestoreObserver observer; 480 public String curPassword; // filled in by the confirmation step 481 public String encryptPassword; 482 483 FullParams() { 484 latch = new AtomicBoolean(false); 485 } 486 } 487 488 class FullBackupParams extends FullParams { 489 public boolean includeApks; 490 public boolean includeObbs; 491 public boolean includeShared; 492 public boolean doWidgets; 493 public boolean allApps; 494 public boolean includeSystem; 495 public boolean doCompress; 496 public String[] packages; 497 498 FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs, 499 boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem, 500 boolean compress, String[] pkgList) { 501 fd = output; 502 includeApks = saveApks; 503 includeObbs = saveObbs; 504 includeShared = saveShared; 505 doWidgets = alsoWidgets; 506 allApps = doAllApps; 507 includeSystem = doSystem; 508 doCompress = compress; 509 packages = pkgList; 510 } 511 } 512 513 class FullRestoreParams extends FullParams { 514 FullRestoreParams(ParcelFileDescriptor input) { 515 fd = input; 516 } 517 } 518 519 // Bookkeeping of in-flight operations for timeout etc. purposes. The operation 520 // token is the index of the entry in the pending-operations list. 521 static final int OP_PENDING = 0; 522 static final int OP_ACKNOWLEDGED = 1; 523 static final int OP_TIMEOUT = -1; 524 525 class Operation { 526 public int state; 527 public BackupRestoreTask callback; 528 529 Operation(int initialState, BackupRestoreTask callbackObj) { 530 state = initialState; 531 callback = callbackObj; 532 } 533 } 534 final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>(); 535 final Object mCurrentOpLock = new Object(); 536 final Random mTokenGenerator = new Random(); 537 538 final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>(); 539 540 // Where we keep our journal files and other bookkeeping 541 File mBaseStateDir; 542 File mDataDir; 543 File mJournalDir; 544 File mJournal; 545 546 // Backup password, if any, and the file where it's saved. What is stored is not the 547 // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but 548 // persisted) salt. Validation is performed by running the challenge text through the 549 // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches 550 // the saved hash string, then the challenge text matches the originally supplied 551 // password text. 552 private final SecureRandom mRng = new SecureRandom(); 553 private String mPasswordHash; 554 private File mPasswordHashFile; 555 private int mPasswordVersion; 556 private File mPasswordVersionFile; 557 private byte[] mPasswordSalt; 558 559 // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys 560 static final int PBKDF2_HASH_ROUNDS = 10000; 561 static final int PBKDF2_KEY_SIZE = 256; // bits 562 static final int PBKDF2_SALT_SIZE = 512; // bits 563 static final String ENCRYPTION_ALGORITHM_NAME = "AES-256"; 564 565 // Keep a log of all the apps we've ever backed up, and what the 566 // dataset tokens are for both the current backup dataset and 567 // the ancestral dataset. 568 private File mEverStored; 569 HashSet<String> mEverStoredApps = new HashSet<String>(); 570 571 static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes 572 File mTokenFile; 573 Set<String> mAncestralPackages = null; 574 long mAncestralToken = 0; 575 long mCurrentToken = 0; 576 577 // Persistently track the need to do a full init 578 static final String INIT_SENTINEL_FILE_NAME = "_need_init_"; 579 HashSet<String> mPendingInits = new HashSet<String>(); // transport names 580 581 // Round-robin queue for scheduling full backup passes 582 static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file 583 class FullBackupEntry implements Comparable<FullBackupEntry> { 584 String packageName; 585 long lastBackup; 586 587 FullBackupEntry(String pkg, long when) { 588 packageName = pkg; 589 lastBackup = when; 590 } 591 592 @Override 593 public int compareTo(FullBackupEntry other) { 594 if (lastBackup < other.lastBackup) return -1; 595 else if (lastBackup > other.lastBackup) return 1; 596 else return 0; 597 } 598 } 599 600 File mFullBackupScheduleFile; 601 // If we're running a schedule-driven full backup, this is the task instance doing it 602 PerformFullTransportBackupTask mRunningFullBackupTask; // inside mQueueLock 603 ArrayList<FullBackupEntry> mFullBackupQueue; // inside mQueueLock 604 605 // Utility: build a new random integer token 606 int generateToken() { 607 int token; 608 do { 609 synchronized (mTokenGenerator) { 610 token = mTokenGenerator.nextInt(); 611 } 612 } while (token < 0); 613 return token; 614 } 615 616 // High level policy: apps are ineligible for backup if certain conditions apply 617 public static boolean appIsEligibleForBackup(ApplicationInfo app) { 618 // 1. their manifest states android:allowBackup="false" 619 if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 620 return false; 621 } 622 623 // 2. they run as a system-level uid but do not supply their own backup agent 624 if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) { 625 return false; 626 } 627 628 // 3. it is the special shared-storage backup package used for 'adb backup' 629 if (app.packageName.equals(BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE)) { 630 return false; 631 } 632 633 return true; 634 } 635 636 /* does *not* check overall backup eligibility policy! */ 637 public static boolean appGetsFullBackup(PackageInfo pkg) { 638 if (pkg.applicationInfo.backupAgentName != null) { 639 // If it has an agent, it gets full backups only if it says so 640 return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0; 641 } 642 643 // No agent means we do full backups for it 644 return true; 645 } 646 647 // ----- Asynchronous backup/restore handler thread ----- 648 649 private class BackupHandler extends Handler { 650 public BackupHandler(Looper looper) { 651 super(looper); 652 } 653 654 public void handleMessage(Message msg) { 655 656 switch (msg.what) { 657 case MSG_RUN_BACKUP: 658 { 659 mLastBackupPass = System.currentTimeMillis(); 660 mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL; 661 662 IBackupTransport transport = getTransport(mCurrentTransport); 663 if (transport == null) { 664 Slog.v(TAG, "Backup requested but no transport available"); 665 synchronized (mQueueLock) { 666 mBackupRunning = false; 667 } 668 mWakelock.release(); 669 break; 670 } 671 672 // snapshot the pending-backup set and work on that 673 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>(); 674 File oldJournal = mJournal; 675 synchronized (mQueueLock) { 676 // Do we have any work to do? Construct the work queue 677 // then release the synchronization lock to actually run 678 // the backup. 679 if (mPendingBackups.size() > 0) { 680 for (BackupRequest b: mPendingBackups.values()) { 681 queue.add(b); 682 } 683 if (DEBUG) Slog.v(TAG, "clearing pending backups"); 684 mPendingBackups.clear(); 685 686 // Start a new backup-queue journal file too 687 mJournal = null; 688 689 } 690 } 691 692 // At this point, we have started a new journal file, and the old 693 // file identity is being passed to the backup processing task. 694 // When it completes successfully, that old journal file will be 695 // deleted. If we crash prior to that, the old journal is parsed 696 // at next boot and the journaled requests fulfilled. 697 boolean staged = true; 698 if (queue.size() > 0) { 699 // Spin up a backup state sequence and set it running 700 try { 701 String dirName = transport.transportDirName(); 702 PerformBackupTask pbt = new PerformBackupTask(transport, dirName, 703 queue, oldJournal); 704 Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt); 705 sendMessage(pbtMessage); 706 } catch (RemoteException e) { 707 // unable to ask the transport its dir name -- transient failure, since 708 // the above check succeeded. Try again next time. 709 Slog.e(TAG, "Transport became unavailable attempting backup"); 710 staged = false; 711 } 712 } else { 713 Slog.v(TAG, "Backup requested but nothing pending"); 714 staged = false; 715 } 716 717 if (!staged) { 718 // if we didn't actually hand off the wakelock, rewind until next time 719 synchronized (mQueueLock) { 720 mBackupRunning = false; 721 } 722 mWakelock.release(); 723 } 724 break; 725 } 726 727 case MSG_BACKUP_RESTORE_STEP: 728 { 729 try { 730 BackupRestoreTask task = (BackupRestoreTask) msg.obj; 731 if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing"); 732 task.execute(); 733 } catch (ClassCastException e) { 734 Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj); 735 } 736 break; 737 } 738 739 case MSG_OP_COMPLETE: 740 { 741 try { 742 BackupRestoreTask task = (BackupRestoreTask) msg.obj; 743 task.operationComplete(); 744 } catch (ClassCastException e) { 745 Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj); 746 } 747 break; 748 } 749 750 case MSG_RUN_ADB_BACKUP: 751 { 752 // TODO: refactor full backup to be a looper-based state machine 753 // similar to normal backup/restore. 754 FullBackupParams params = (FullBackupParams)msg.obj; 755 PerformAdbBackupTask task = new PerformAdbBackupTask(params.fd, 756 params.observer, params.includeApks, params.includeObbs, 757 params.includeShared, params.doWidgets, 758 params.curPassword, params.encryptPassword, 759 params.allApps, params.includeSystem, params.doCompress, 760 params.packages, params.latch); 761 (new Thread(task, "adb-backup")).start(); 762 break; 763 } 764 765 case MSG_RUN_FULL_TRANSPORT_BACKUP: 766 { 767 PerformFullTransportBackupTask task = (PerformFullTransportBackupTask) msg.obj; 768 (new Thread(task, "transport-backup")).start(); 769 break; 770 } 771 772 case MSG_RUN_RESTORE: 773 { 774 RestoreParams params = (RestoreParams)msg.obj; 775 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer); 776 BackupRestoreTask task = new PerformUnifiedRestoreTask(params.transport, 777 params.observer, params.token, params.pkgInfo, params.pmToken, 778 params.isSystemRestore, params.filterSet); 779 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task); 780 sendMessage(restoreMsg); 781 break; 782 } 783 784 case MSG_RUN_ADB_RESTORE: 785 { 786 // TODO: refactor full restore to be a looper-based state machine 787 // similar to normal backup/restore. 788 FullRestoreParams params = (FullRestoreParams)msg.obj; 789 PerformAdbRestoreTask task = new PerformAdbRestoreTask(params.fd, 790 params.curPassword, params.encryptPassword, 791 params.observer, params.latch); 792 (new Thread(task, "adb-restore")).start(); 793 break; 794 } 795 796 case MSG_RUN_CLEAR: 797 { 798 ClearParams params = (ClearParams)msg.obj; 799 (new PerformClearTask(params.transport, params.packageInfo)).run(); 800 break; 801 } 802 803 case MSG_RETRY_CLEAR: 804 { 805 // reenqueues if the transport remains unavailable 806 ClearRetryParams params = (ClearRetryParams)msg.obj; 807 clearBackupData(params.transportName, params.packageName); 808 break; 809 } 810 811 case MSG_RUN_INITIALIZE: 812 { 813 HashSet<String> queue; 814 815 // Snapshot the pending-init queue and work on that 816 synchronized (mQueueLock) { 817 queue = new HashSet<String>(mPendingInits); 818 mPendingInits.clear(); 819 } 820 821 (new PerformInitializeTask(queue)).run(); 822 break; 823 } 824 825 case MSG_RETRY_INIT: 826 { 827 synchronized (mQueueLock) { 828 recordInitPendingLocked(msg.arg1 != 0, (String)msg.obj); 829 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 830 mRunInitIntent); 831 } 832 break; 833 } 834 835 case MSG_RUN_GET_RESTORE_SETS: 836 { 837 // Like other async operations, this is entered with the wakelock held 838 RestoreSet[] sets = null; 839 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj; 840 try { 841 sets = params.transport.getAvailableRestoreSets(); 842 // cache the result in the active session 843 synchronized (params.session) { 844 params.session.mRestoreSets = sets; 845 } 846 if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 847 } catch (Exception e) { 848 Slog.e(TAG, "Error from transport getting set list"); 849 } finally { 850 if (params.observer != null) { 851 try { 852 params.observer.restoreSetsAvailable(sets); 853 } catch (RemoteException re) { 854 Slog.e(TAG, "Unable to report listing to observer"); 855 } catch (Exception e) { 856 Slog.e(TAG, "Restore observer threw", e); 857 } 858 } 859 860 // Done: reset the session timeout clock 861 removeMessages(MSG_RESTORE_TIMEOUT); 862 sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 863 864 mWakelock.release(); 865 } 866 break; 867 } 868 869 case MSG_TIMEOUT: 870 { 871 handleTimeout(msg.arg1, msg.obj); 872 break; 873 } 874 875 case MSG_RESTORE_TIMEOUT: 876 { 877 synchronized (BackupManagerService.this) { 878 if (mActiveRestoreSession != null) { 879 // Client app left the restore session dangling. We know that it 880 // can't be in the middle of an actual restore operation because 881 // the timeout is suspended while a restore is in progress. Clean 882 // up now. 883 Slog.w(TAG, "Restore session timed out; aborting"); 884 mActiveRestoreSession.markTimedOut(); 885 post(mActiveRestoreSession.new EndRestoreRunnable( 886 BackupManagerService.this, mActiveRestoreSession)); 887 } 888 } 889 break; 890 } 891 892 case MSG_FULL_CONFIRMATION_TIMEOUT: 893 { 894 synchronized (mFullConfirmations) { 895 FullParams params = mFullConfirmations.get(msg.arg1); 896 if (params != null) { 897 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation"); 898 899 // Release the waiter; timeout == completion 900 signalFullBackupRestoreCompletion(params); 901 902 // Remove the token from the set 903 mFullConfirmations.delete(msg.arg1); 904 905 // Report a timeout to the observer, if any 906 if (params.observer != null) { 907 try { 908 params.observer.onTimeout(); 909 } catch (RemoteException e) { 910 /* don't care if the app has gone away */ 911 } 912 } 913 } else { 914 Slog.d(TAG, "couldn't find params for token " + msg.arg1); 915 } 916 } 917 break; 918 } 919 920 case MSG_WIDGET_BROADCAST: 921 { 922 final Intent intent = (Intent) msg.obj; 923 mContext.sendBroadcastAsUser(intent, UserHandle.OWNER); 924 break; 925 } 926 } 927 } 928 } 929 930 // ----- Debug-only backup operation trace ----- 931 void addBackupTrace(String s) { 932 if (DEBUG_BACKUP_TRACE) { 933 synchronized (mBackupTrace) { 934 mBackupTrace.add(s); 935 } 936 } 937 } 938 939 void clearBackupTrace() { 940 if (DEBUG_BACKUP_TRACE) { 941 synchronized (mBackupTrace) { 942 mBackupTrace.clear(); 943 } 944 } 945 } 946 947 // ----- Main service implementation ----- 948 949 public BackupManagerService(Context context, Trampoline parent) { 950 mContext = context; 951 mPackageManager = context.getPackageManager(); 952 mPackageManagerBinder = AppGlobals.getPackageManager(); 953 mActivityManager = ActivityManagerNative.getDefault(); 954 955 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 956 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 957 mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount")); 958 959 mBackupManagerBinder = Trampoline.asInterface(parent.asBinder()); 960 961 // spin up the backup/restore handler thread 962 mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND); 963 mHandlerThread.start(); 964 mBackupHandler = new BackupHandler(mHandlerThread.getLooper()); 965 966 // Set up our bookkeeping 967 final ContentResolver resolver = context.getContentResolver(); 968 mProvisioned = Settings.Global.getInt(resolver, 969 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 970 mAutoRestore = Settings.Secure.getInt(resolver, 971 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0; 972 973 mProvisionedObserver = new ProvisionedObserver(mBackupHandler); 974 resolver.registerContentObserver( 975 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), 976 false, mProvisionedObserver); 977 978 // If Encrypted file systems is enabled or disabled, this call will return the 979 // correct directory. 980 mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup"); 981 mBaseStateDir.mkdirs(); 982 if (!SELinux.restorecon(mBaseStateDir)) { 983 Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir); 984 } 985 mDataDir = Environment.getDownloadCacheDirectory(); 986 987 mPasswordVersion = 1; // unless we hear otherwise 988 mPasswordVersionFile = new File(mBaseStateDir, "pwversion"); 989 if (mPasswordVersionFile.exists()) { 990 FileInputStream fin = null; 991 DataInputStream in = null; 992 try { 993 fin = new FileInputStream(mPasswordVersionFile); 994 in = new DataInputStream(fin); 995 mPasswordVersion = in.readInt(); 996 } catch (IOException e) { 997 Slog.e(TAG, "Unable to read backup pw version"); 998 } finally { 999 try { 1000 if (in != null) in.close(); 1001 if (fin != null) fin.close(); 1002 } catch (IOException e) { 1003 Slog.w(TAG, "Error closing pw version files"); 1004 } 1005 } 1006 } 1007 1008 mPasswordHashFile = new File(mBaseStateDir, "pwhash"); 1009 if (mPasswordHashFile.exists()) { 1010 FileInputStream fin = null; 1011 DataInputStream in = null; 1012 try { 1013 fin = new FileInputStream(mPasswordHashFile); 1014 in = new DataInputStream(new BufferedInputStream(fin)); 1015 // integer length of the salt array, followed by the salt, 1016 // then the hex pw hash string 1017 int saltLen = in.readInt(); 1018 byte[] salt = new byte[saltLen]; 1019 in.readFully(salt); 1020 mPasswordHash = in.readUTF(); 1021 mPasswordSalt = salt; 1022 } catch (IOException e) { 1023 Slog.e(TAG, "Unable to read saved backup pw hash"); 1024 } finally { 1025 try { 1026 if (in != null) in.close(); 1027 if (fin != null) fin.close(); 1028 } catch (IOException e) { 1029 Slog.w(TAG, "Unable to close streams"); 1030 } 1031 } 1032 } 1033 1034 // Alarm receivers for scheduled backups & initialization operations 1035 mRunBackupReceiver = new RunBackupReceiver(); 1036 IntentFilter filter = new IntentFilter(); 1037 filter.addAction(RUN_BACKUP_ACTION); 1038 context.registerReceiver(mRunBackupReceiver, filter, 1039 android.Manifest.permission.BACKUP, null); 1040 1041 mRunInitReceiver = new RunInitializeReceiver(); 1042 filter = new IntentFilter(); 1043 filter.addAction(RUN_INITIALIZE_ACTION); 1044 context.registerReceiver(mRunInitReceiver, filter, 1045 android.Manifest.permission.BACKUP, null); 1046 1047 Intent backupIntent = new Intent(RUN_BACKUP_ACTION); 1048 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 1049 mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0); 1050 1051 Intent initIntent = new Intent(RUN_INITIALIZE_ACTION); 1052 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 1053 mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0); 1054 1055 // Set up the backup-request journaling 1056 mJournalDir = new File(mBaseStateDir, "pending"); 1057 mJournalDir.mkdirs(); // creates mBaseStateDir along the way 1058 mJournal = null; // will be created on first use 1059 1060 // Set up the various sorts of package tracking we do 1061 mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule"); 1062 initPackageTracking(); 1063 1064 // Build our mapping of uid to backup client services. This implicitly 1065 // schedules a backup pass on the Package Manager metadata the first 1066 // time anything needs to be backed up. 1067 synchronized (mBackupParticipants) { 1068 addPackageParticipantsLocked(null); 1069 } 1070 1071 // Set up our transport options and initialize the default transport 1072 // TODO: Don't create transports that we don't need to? 1073 mCurrentTransport = Settings.Secure.getString(context.getContentResolver(), 1074 Settings.Secure.BACKUP_TRANSPORT); 1075 if ("".equals(mCurrentTransport)) { 1076 mCurrentTransport = null; 1077 } 1078 if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport); 1079 1080 // Find all transport hosts and bind to their services 1081 List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser( 1082 mTransportServiceIntent, 0, UserHandle.USER_OWNER); 1083 if (DEBUG) { 1084 Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size())); 1085 } 1086 if (hosts != null) { 1087 for (int i = 0; i < hosts.size(); i++) { 1088 final ServiceInfo transport = hosts.get(i).serviceInfo; 1089 if (MORE_DEBUG) { 1090 Slog.v(TAG, " " + transport.packageName + "/" + transport.name); 1091 } 1092 tryBindTransport(transport); 1093 } 1094 } 1095 1096 // Now that we know about valid backup participants, parse any 1097 // leftover journal files into the pending backup set 1098 parseLeftoverJournals(); 1099 1100 // Power management 1101 mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"); 1102 } 1103 1104 private class RunBackupReceiver extends BroadcastReceiver { 1105 public void onReceive(Context context, Intent intent) { 1106 if (RUN_BACKUP_ACTION.equals(intent.getAction())) { 1107 synchronized (mQueueLock) { 1108 if (mPendingInits.size() > 0) { 1109 // If there are pending init operations, we process those 1110 // and then settle into the usual periodic backup schedule. 1111 if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup"); 1112 try { 1113 mAlarmManager.cancel(mRunInitIntent); 1114 mRunInitIntent.send(); 1115 } catch (PendingIntent.CanceledException ce) { 1116 Slog.e(TAG, "Run init intent cancelled"); 1117 // can't really do more than bail here 1118 } 1119 } else { 1120 // Don't run backups now if we're disabled or not yet 1121 // fully set up. 1122 if (mEnabled && mProvisioned) { 1123 if (!mBackupRunning) { 1124 if (DEBUG) Slog.v(TAG, "Running a backup pass"); 1125 1126 // Acquire the wakelock and pass it to the backup thread. it will 1127 // be released once backup concludes. 1128 mBackupRunning = true; 1129 mWakelock.acquire(); 1130 1131 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP); 1132 mBackupHandler.sendMessage(msg); 1133 } else { 1134 Slog.i(TAG, "Backup time but one already running"); 1135 } 1136 } else { 1137 Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned); 1138 } 1139 } 1140 } 1141 } 1142 } 1143 } 1144 1145 private class RunInitializeReceiver extends BroadcastReceiver { 1146 public void onReceive(Context context, Intent intent) { 1147 if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) { 1148 synchronized (mQueueLock) { 1149 if (DEBUG) Slog.v(TAG, "Running a device init"); 1150 1151 // Acquire the wakelock and pass it to the init thread. it will 1152 // be released once init concludes. 1153 mWakelock.acquire(); 1154 1155 Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE); 1156 mBackupHandler.sendMessage(msg); 1157 } 1158 } 1159 } 1160 } 1161 1162 private void initPackageTracking() { 1163 if (MORE_DEBUG) Slog.v(TAG, "` tracking"); 1164 1165 // Remember our ancestral dataset 1166 mTokenFile = new File(mBaseStateDir, "ancestral"); 1167 try { 1168 RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r"); 1169 int version = tf.readInt(); 1170 if (version == CURRENT_ANCESTRAL_RECORD_VERSION) { 1171 mAncestralToken = tf.readLong(); 1172 mCurrentToken = tf.readLong(); 1173 1174 int numPackages = tf.readInt(); 1175 if (numPackages >= 0) { 1176 mAncestralPackages = new HashSet<String>(); 1177 for (int i = 0; i < numPackages; i++) { 1178 String pkgName = tf.readUTF(); 1179 mAncestralPackages.add(pkgName); 1180 } 1181 } 1182 } 1183 tf.close(); 1184 } catch (FileNotFoundException fnf) { 1185 // Probably innocuous 1186 Slog.v(TAG, "No ancestral data"); 1187 } catch (IOException e) { 1188 Slog.w(TAG, "Unable to read token file", e); 1189 } 1190 1191 // Keep a log of what apps we've ever backed up. Because we might have 1192 // rebooted in the middle of an operation that was removing something from 1193 // this log, we sanity-check its contents here and reconstruct it. 1194 mEverStored = new File(mBaseStateDir, "processed"); 1195 File tempProcessedFile = new File(mBaseStateDir, "processed.new"); 1196 1197 // If we were in the middle of removing something from the ever-backed-up 1198 // file, there might be a transient "processed.new" file still present. 1199 // Ignore it -- we'll validate "processed" against the current package set. 1200 if (tempProcessedFile.exists()) { 1201 tempProcessedFile.delete(); 1202 } 1203 1204 // If there are previous contents, parse them out then start a new 1205 // file to continue the recordkeeping. 1206 if (mEverStored.exists()) { 1207 RandomAccessFile temp = null; 1208 RandomAccessFile in = null; 1209 1210 try { 1211 temp = new RandomAccessFile(tempProcessedFile, "rws"); 1212 in = new RandomAccessFile(mEverStored, "r"); 1213 1214 while (true) { 1215 PackageInfo info; 1216 String pkg = in.readUTF(); 1217 try { 1218 info = mPackageManager.getPackageInfo(pkg, 0); 1219 mEverStoredApps.add(pkg); 1220 temp.writeUTF(pkg); 1221 if (MORE_DEBUG) Slog.v(TAG, " + " + pkg); 1222 } catch (NameNotFoundException e) { 1223 // nope, this package was uninstalled; don't include it 1224 if (MORE_DEBUG) Slog.v(TAG, " - " + pkg); 1225 } 1226 } 1227 } catch (EOFException e) { 1228 // Once we've rewritten the backup history log, atomically replace the 1229 // old one with the new one then reopen the file for continuing use. 1230 if (!tempProcessedFile.renameTo(mEverStored)) { 1231 Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored); 1232 } 1233 } catch (IOException e) { 1234 Slog.e(TAG, "Error in processed file", e); 1235 } finally { 1236 try { if (temp != null) temp.close(); } catch (IOException e) {} 1237 try { if (in != null) in.close(); } catch (IOException e) {} 1238 } 1239 } 1240 1241 // Resume the full-data backup queue 1242 mFullBackupQueue = readFullBackupSchedule(); 1243 1244 // Register for broadcasts about package install, etc., so we can 1245 // update the provider list. 1246 IntentFilter filter = new IntentFilter(); 1247 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 1248 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1249 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1250 filter.addDataScheme("package"); 1251 mContext.registerReceiver(mBroadcastReceiver, filter); 1252 // Register for events related to sdcard installation. 1253 IntentFilter sdFilter = new IntentFilter(); 1254 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 1255 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 1256 mContext.registerReceiver(mBroadcastReceiver, sdFilter); 1257 } 1258 1259 private ArrayList<FullBackupEntry> readFullBackupSchedule() { 1260 ArrayList<FullBackupEntry> schedule = null; 1261 synchronized (mQueueLock) { 1262 if (mFullBackupScheduleFile.exists()) { 1263 FileInputStream fstream = null; 1264 BufferedInputStream bufStream = null; 1265 DataInputStream in = null; 1266 try { 1267 fstream = new FileInputStream(mFullBackupScheduleFile); 1268 bufStream = new BufferedInputStream(fstream); 1269 in = new DataInputStream(bufStream); 1270 1271 int version = in.readInt(); 1272 if (version != SCHEDULE_FILE_VERSION) { 1273 Slog.e(TAG, "Unknown backup schedule version " + version); 1274 return null; 1275 } 1276 1277 int N = in.readInt(); 1278 schedule = new ArrayList<FullBackupEntry>(N); 1279 for (int i = 0; i < N; i++) { 1280 String pkgName = in.readUTF(); 1281 long lastBackup = in.readLong(); 1282 schedule.add(new FullBackupEntry(pkgName, lastBackup)); 1283 } 1284 Collections.sort(schedule); 1285 } catch (Exception e) { 1286 Slog.e(TAG, "Unable to read backup schedule", e); 1287 mFullBackupScheduleFile.delete(); 1288 schedule = null; 1289 } finally { 1290 IoUtils.closeQuietly(in); 1291 IoUtils.closeQuietly(bufStream); 1292 IoUtils.closeQuietly(fstream); 1293 } 1294 } 1295 1296 if (schedule == null) { 1297 // no prior queue record, or unable to read it. Set up the queue 1298 // from scratch. 1299 List<PackageInfo> apps = 1300 PackageManagerBackupAgent.getStorableApplications(mPackageManager); 1301 final int N = apps.size(); 1302 schedule = new ArrayList<FullBackupEntry>(N); 1303 for (int i = 0; i < N; i++) { 1304 PackageInfo info = apps.get(i); 1305 if (appGetsFullBackup(info)) { 1306 schedule.add(new FullBackupEntry(info.packageName, 0)); 1307 } 1308 } 1309 writeFullBackupScheduleAsync(); 1310 } 1311 } 1312 return schedule; 1313 } 1314 1315 Runnable mFullBackupScheduleWriter = new Runnable() { 1316 @Override public void run() { 1317 synchronized (mQueueLock) { 1318 try { 1319 ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096); 1320 DataOutputStream bufOut = new DataOutputStream(bufStream); 1321 bufOut.writeInt(SCHEDULE_FILE_VERSION); 1322 1323 // version 1: 1324 // 1325 // [int] # of packages in the queue = N 1326 // N * { 1327 // [utf8] package name 1328 // [long] last backup time for this package 1329 // } 1330 int N = mFullBackupQueue.size(); 1331 bufOut.writeInt(N); 1332 1333 for (int i = 0; i < N; i++) { 1334 FullBackupEntry entry = mFullBackupQueue.get(i); 1335 bufOut.writeUTF(entry.packageName); 1336 bufOut.writeLong(entry.lastBackup); 1337 } 1338 bufOut.flush(); 1339 1340 AtomicFile af = new AtomicFile(mFullBackupScheduleFile); 1341 FileOutputStream out = af.startWrite(); 1342 out.write(bufStream.toByteArray()); 1343 af.finishWrite(out); 1344 } catch (Exception e) { 1345 Slog.e(TAG, "Unable to write backup schedule!", e); 1346 } 1347 } 1348 } 1349 }; 1350 1351 private void writeFullBackupScheduleAsync() { 1352 mBackupHandler.removeCallbacks(mFullBackupScheduleWriter); 1353 mBackupHandler.post(mFullBackupScheduleWriter); 1354 } 1355 1356 private void parseLeftoverJournals() { 1357 for (File f : mJournalDir.listFiles()) { 1358 if (mJournal == null || f.compareTo(mJournal) != 0) { 1359 // This isn't the current journal, so it must be a leftover. Read 1360 // out the package names mentioned there and schedule them for 1361 // backup. 1362 RandomAccessFile in = null; 1363 try { 1364 Slog.i(TAG, "Found stale backup journal, scheduling"); 1365 in = new RandomAccessFile(f, "r"); 1366 while (true) { 1367 String packageName = in.readUTF(); 1368 if (MORE_DEBUG) Slog.i(TAG, " " + packageName); 1369 dataChangedImpl(packageName); 1370 } 1371 } catch (EOFException e) { 1372 // no more data; we're done 1373 } catch (Exception e) { 1374 Slog.e(TAG, "Can't read " + f, e); 1375 } finally { 1376 // close/delete the file 1377 try { if (in != null) in.close(); } catch (IOException e) {} 1378 f.delete(); 1379 } 1380 } 1381 } 1382 } 1383 1384 private SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) { 1385 return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds); 1386 } 1387 1388 private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) { 1389 try { 1390 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm); 1391 KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE); 1392 return keyFactory.generateSecret(ks); 1393 } catch (InvalidKeySpecException e) { 1394 Slog.e(TAG, "Invalid key spec for PBKDF2!"); 1395 } catch (NoSuchAlgorithmException e) { 1396 Slog.e(TAG, "PBKDF2 unavailable!"); 1397 } 1398 return null; 1399 } 1400 1401 private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) { 1402 SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds); 1403 if (key != null) { 1404 return byteArrayToHex(key.getEncoded()); 1405 } 1406 return null; 1407 } 1408 1409 private String byteArrayToHex(byte[] data) { 1410 StringBuilder buf = new StringBuilder(data.length * 2); 1411 for (int i = 0; i < data.length; i++) { 1412 buf.append(Byte.toHexString(data[i], true)); 1413 } 1414 return buf.toString(); 1415 } 1416 1417 private byte[] hexToByteArray(String digits) { 1418 final int bytes = digits.length() / 2; 1419 if (2*bytes != digits.length()) { 1420 throw new IllegalArgumentException("Hex string must have an even number of digits"); 1421 } 1422 1423 byte[] result = new byte[bytes]; 1424 for (int i = 0; i < digits.length(); i += 2) { 1425 result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16); 1426 } 1427 return result; 1428 } 1429 1430 private byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) { 1431 char[] mkAsChar = new char[pwBytes.length]; 1432 for (int i = 0; i < pwBytes.length; i++) { 1433 mkAsChar[i] = (char) pwBytes[i]; 1434 } 1435 1436 Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds); 1437 return checksum.getEncoded(); 1438 } 1439 1440 // Used for generating random salts or passwords 1441 private byte[] randomBytes(int bits) { 1442 byte[] array = new byte[bits / 8]; 1443 mRng.nextBytes(array); 1444 return array; 1445 } 1446 1447 boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) { 1448 if (mPasswordHash == null) { 1449 // no current password case -- require that 'currentPw' be null or empty 1450 if (candidatePw == null || "".equals(candidatePw)) { 1451 return true; 1452 } // else the non-empty candidate does not match the empty stored pw 1453 } else { 1454 // hash the stated current pw and compare to the stored one 1455 if (candidatePw != null && candidatePw.length() > 0) { 1456 String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds); 1457 if (mPasswordHash.equalsIgnoreCase(currentPwHash)) { 1458 // candidate hash matches the stored hash -- the password matches 1459 return true; 1460 } 1461 } // else the stored pw is nonempty but the candidate is empty; no match 1462 } 1463 return false; 1464 } 1465 1466 public boolean setBackupPassword(String currentPw, String newPw) { 1467 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 1468 "setBackupPassword"); 1469 1470 // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes 1471 final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION); 1472 1473 // If the supplied pw doesn't hash to the the saved one, fail. The password 1474 // might be caught in the legacy crypto mismatch; verify that too. 1475 if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) 1476 && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, 1477 currentPw, PBKDF2_HASH_ROUNDS))) { 1478 return false; 1479 } 1480 1481 // Snap up to current on the pw file version 1482 mPasswordVersion = BACKUP_PW_FILE_VERSION; 1483 FileOutputStream pwFout = null; 1484 DataOutputStream pwOut = null; 1485 try { 1486 pwFout = new FileOutputStream(mPasswordVersionFile); 1487 pwOut = new DataOutputStream(pwFout); 1488 pwOut.writeInt(mPasswordVersion); 1489 } catch (IOException e) { 1490 Slog.e(TAG, "Unable to write backup pw version; password not changed"); 1491 return false; 1492 } finally { 1493 try { 1494 if (pwOut != null) pwOut.close(); 1495 if (pwFout != null) pwFout.close(); 1496 } catch (IOException e) { 1497 Slog.w(TAG, "Unable to close pw version record"); 1498 } 1499 } 1500 1501 // Clearing the password is okay 1502 if (newPw == null || newPw.isEmpty()) { 1503 if (mPasswordHashFile.exists()) { 1504 if (!mPasswordHashFile.delete()) { 1505 // Unable to delete the old pw file, so fail 1506 Slog.e(TAG, "Unable to clear backup password"); 1507 return false; 1508 } 1509 } 1510 mPasswordHash = null; 1511 mPasswordSalt = null; 1512 return true; 1513 } 1514 1515 try { 1516 // Okay, build the hash of the new backup password 1517 byte[] salt = randomBytes(PBKDF2_SALT_SIZE); 1518 String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS); 1519 1520 OutputStream pwf = null, buffer = null; 1521 DataOutputStream out = null; 1522 try { 1523 pwf = new FileOutputStream(mPasswordHashFile); 1524 buffer = new BufferedOutputStream(pwf); 1525 out = new DataOutputStream(buffer); 1526 // integer length of the salt array, followed by the salt, 1527 // then the hex pw hash string 1528 out.writeInt(salt.length); 1529 out.write(salt); 1530 out.writeUTF(newPwHash); 1531 out.flush(); 1532 mPasswordHash = newPwHash; 1533 mPasswordSalt = salt; 1534 return true; 1535 } finally { 1536 if (out != null) out.close(); 1537 if (buffer != null) buffer.close(); 1538 if (pwf != null) pwf.close(); 1539 } 1540 } catch (IOException e) { 1541 Slog.e(TAG, "Unable to set backup password"); 1542 } 1543 return false; 1544 } 1545 1546 public boolean hasBackupPassword() { 1547 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 1548 "hasBackupPassword"); 1549 1550 return mPasswordHash != null && mPasswordHash.length() > 0; 1551 } 1552 1553 private boolean backupPasswordMatches(String currentPw) { 1554 if (hasBackupPassword()) { 1555 final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION); 1556 if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS) 1557 && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK, 1558 currentPw, PBKDF2_HASH_ROUNDS))) { 1559 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 1560 return false; 1561 } 1562 } 1563 return true; 1564 } 1565 1566 // Maintain persistent state around whether need to do an initialize operation. 1567 // Must be called with the queue lock held. 1568 void recordInitPendingLocked(boolean isPending, String transportName) { 1569 if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending 1570 + " on transport " + transportName); 1571 mBackupHandler.removeMessages(MSG_RETRY_INIT); 1572 1573 try { 1574 IBackupTransport transport = getTransport(transportName); 1575 if (transport != null) { 1576 String transportDirName = transport.transportDirName(); 1577 File stateDir = new File(mBaseStateDir, transportDirName); 1578 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME); 1579 1580 if (isPending) { 1581 // We need an init before we can proceed with sending backup data. 1582 // Record that with an entry in our set of pending inits, as well as 1583 // journaling it via creation of a sentinel file. 1584 mPendingInits.add(transportName); 1585 try { 1586 (new FileOutputStream(initPendingFile)).close(); 1587 } catch (IOException ioe) { 1588 // Something is badly wrong with our permissions; just try to move on 1589 } 1590 } else { 1591 // No more initialization needed; wipe the journal and reset our state. 1592 initPendingFile.delete(); 1593 mPendingInits.remove(transportName); 1594 } 1595 return; // done; don't fall through to the error case 1596 } 1597 } catch (RemoteException e) { 1598 // transport threw when asked its name; fall through to the lookup-failed case 1599 } 1600 1601 // The named transport doesn't exist or threw. This operation is 1602 // important, so we record the need for a an init and post a message 1603 // to retry the init later. 1604 if (isPending) { 1605 mPendingInits.add(transportName); 1606 mBackupHandler.sendMessageDelayed( 1607 mBackupHandler.obtainMessage(MSG_RETRY_INIT, 1608 (isPending ? 1 : 0), 1609 0, 1610 transportName), 1611 TRANSPORT_RETRY_INTERVAL); 1612 } 1613 } 1614 1615 // Reset all of our bookkeeping, in response to having been told that 1616 // the backend data has been wiped [due to idle expiry, for example], 1617 // so we must re-upload all saved settings. 1618 void resetBackupState(File stateFileDir) { 1619 synchronized (mQueueLock) { 1620 // Wipe the "what we've ever backed up" tracking 1621 mEverStoredApps.clear(); 1622 mEverStored.delete(); 1623 1624 mCurrentToken = 0; 1625 writeRestoreTokens(); 1626 1627 // Remove all the state files 1628 for (File sf : stateFileDir.listFiles()) { 1629 // ... but don't touch the needs-init sentinel 1630 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) { 1631 sf.delete(); 1632 } 1633 } 1634 } 1635 1636 // Enqueue a new backup of every participant 1637 synchronized (mBackupParticipants) { 1638 final int N = mBackupParticipants.size(); 1639 for (int i=0; i<N; i++) { 1640 HashSet<String> participants = mBackupParticipants.valueAt(i); 1641 if (participants != null) { 1642 for (String packageName : participants) { 1643 dataChangedImpl(packageName); 1644 } 1645 } 1646 } 1647 } 1648 } 1649 1650 // Add a transport to our set of available backends. If 'transport' is null, this 1651 // is an unregistration, and the transport's entry is removed from our bookkeeping. 1652 private void registerTransport(String name, String component, IBackupTransport transport) { 1653 synchronized (mTransports) { 1654 if (DEBUG) Slog.v(TAG, "Registering transport " 1655 + component + "::" + name + " = " + transport); 1656 if (transport != null) { 1657 mTransports.put(name, transport); 1658 mTransportNames.put(component, name); 1659 } else { 1660 mTransports.remove(mTransportNames.get(component)); 1661 mTransportNames.remove(component); 1662 // Nothing further to do in the unregistration case 1663 return; 1664 } 1665 } 1666 1667 // If the init sentinel file exists, we need to be sure to perform the init 1668 // as soon as practical. We also create the state directory at registration 1669 // time to ensure it's present from the outset. 1670 try { 1671 String transportName = transport.transportDirName(); 1672 File stateDir = new File(mBaseStateDir, transportName); 1673 stateDir.mkdirs(); 1674 1675 File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME); 1676 if (initSentinel.exists()) { 1677 synchronized (mQueueLock) { 1678 mPendingInits.add(transportName); 1679 1680 // TODO: pick a better starting time than now + 1 minute 1681 long delay = 1000 * 60; // one minute, in milliseconds 1682 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 1683 System.currentTimeMillis() + delay, mRunInitIntent); 1684 } 1685 } 1686 } catch (RemoteException e) { 1687 // the transport threw when asked its file naming prefs; declare it invalid 1688 Slog.e(TAG, "Unable to register transport as " + name); 1689 mTransportNames.remove(component); 1690 mTransports.remove(name); 1691 } 1692 } 1693 1694 // ----- Track installation/removal of packages ----- 1695 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1696 public void onReceive(Context context, Intent intent) { 1697 if (DEBUG) Slog.d(TAG, "Received broadcast " + intent); 1698 1699 String action = intent.getAction(); 1700 boolean replacing = false; 1701 boolean added = false; 1702 boolean changed = false; 1703 Bundle extras = intent.getExtras(); 1704 String pkgList[] = null; 1705 if (Intent.ACTION_PACKAGE_ADDED.equals(action) || 1706 Intent.ACTION_PACKAGE_REMOVED.equals(action) || 1707 Intent.ACTION_PACKAGE_CHANGED.equals(action)) { 1708 Uri uri = intent.getData(); 1709 if (uri == null) { 1710 return; 1711 } 1712 String pkgName = uri.getSchemeSpecificPart(); 1713 if (pkgName != null) { 1714 pkgList = new String[] { pkgName }; 1715 } 1716 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action); 1717 1718 // At package-changed we only care about looking at new transport states 1719 if (changed) { 1720 try { 1721 if (MORE_DEBUG) { 1722 Slog.i(TAG, "Package " + pkgName + " changed; rechecking"); 1723 } 1724 // unbind existing possibly-stale connections to that package's transports 1725 synchronized (mTransports) { 1726 TransportConnection conn = mTransportConnections.get(pkgName); 1727 if (conn != null) { 1728 final ServiceInfo svc = conn.mTransport; 1729 ComponentName svcName = 1730 new ComponentName(svc.packageName, svc.name); 1731 String flatName = svcName.flattenToShortString(); 1732 Slog.i(TAG, "Unbinding " + svcName); 1733 1734 mContext.unbindService(conn); 1735 mTransportConnections.remove(pkgName); 1736 mTransports.remove(mTransportNames.get(flatName)); 1737 mTransportNames.remove(flatName); 1738 } 1739 } 1740 // and then (re)bind as appropriate 1741 PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0); 1742 checkForTransportAndBind(app); 1743 } catch (NameNotFoundException e) { 1744 // Nope, can't find it - just ignore 1745 if (MORE_DEBUG) { 1746 Slog.w(TAG, "Can't find changed package " + pkgName); 1747 } 1748 } 1749 return; // nothing more to do in the PACKAGE_CHANGED case 1750 } 1751 1752 added = Intent.ACTION_PACKAGE_ADDED.equals(action); 1753 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); 1754 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { 1755 added = true; 1756 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1757 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { 1758 added = false; 1759 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1760 } 1761 1762 if (pkgList == null || pkgList.length == 0) { 1763 return; 1764 } 1765 1766 final int uid = extras.getInt(Intent.EXTRA_UID); 1767 if (added) { 1768 synchronized (mBackupParticipants) { 1769 if (replacing) { 1770 // This is the package-replaced case; we just remove the entry 1771 // under the old uid and fall through to re-add. 1772 removePackageParticipantsLocked(pkgList, uid); 1773 } 1774 addPackageParticipantsLocked(pkgList); 1775 } 1776 // If they're full-backup candidates, add them there instead 1777 for (String packageName : pkgList) { 1778 try { 1779 PackageInfo app = mPackageManager.getPackageInfo(packageName, 0); 1780 long now = System.currentTimeMillis(); 1781 if (appGetsFullBackup(app)) { 1782 enqueueFullBackup(packageName, now); 1783 scheduleNextFullBackupJob(); 1784 } 1785 1786 // Transport maintenance: rebind to known existing transports that have 1787 // just been updated; and bind to any newly-installed transport services. 1788 synchronized (mTransports) { 1789 final TransportConnection conn = mTransportConnections.get(packageName); 1790 if (conn != null) { 1791 if (MORE_DEBUG) { 1792 Slog.i(TAG, "Transport package changed; rebinding"); 1793 } 1794 bindTransport(conn.mTransport); 1795 } else { 1796 checkForTransportAndBind(app); 1797 } 1798 } 1799 1800 } catch (NameNotFoundException e) { 1801 // doesn't really exist; ignore it 1802 if (DEBUG) { 1803 Slog.i(TAG, "Can't resolve new app " + packageName); 1804 } 1805 } 1806 } 1807 1808 } else { 1809 if (replacing) { 1810 // The package is being updated. We'll receive a PACKAGE_ADDED shortly. 1811 } else { 1812 synchronized (mBackupParticipants) { 1813 removePackageParticipantsLocked(pkgList, uid); 1814 } 1815 } 1816 } 1817 } 1818 }; 1819 1820 // ----- Track connection to transports service ----- 1821 class TransportConnection implements ServiceConnection { 1822 ServiceInfo mTransport; 1823 1824 public TransportConnection(ServiceInfo transport) { 1825 mTransport = transport; 1826 } 1827 1828 @Override 1829 public void onServiceConnected(ComponentName component, IBinder service) { 1830 if (DEBUG) Slog.v(TAG, "Connected to transport " + component); 1831 final String name = component.flattenToShortString(); 1832 try { 1833 IBackupTransport transport = IBackupTransport.Stub.asInterface(service); 1834 registerTransport(transport.name(), name, transport); 1835 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 1); 1836 } catch (RemoteException e) { 1837 Slog.e(TAG, "Unable to register transport " + component); 1838 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0); 1839 } 1840 } 1841 1842 @Override 1843 public void onServiceDisconnected(ComponentName component) { 1844 if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component); 1845 final String name = component.flattenToShortString(); 1846 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0); 1847 registerTransport(null, name, null); 1848 } 1849 }; 1850 1851 // Check whether the given package hosts a transport, and bind if so 1852 void checkForTransportAndBind(PackageInfo pkgInfo) { 1853 Intent intent = new Intent(mTransportServiceIntent) 1854 .setPackage(pkgInfo.packageName); 1855 List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser( 1856 intent, 0, UserHandle.USER_OWNER); 1857 final int N = hosts.size(); 1858 for (int i = 0; i < N; i++) { 1859 final ServiceInfo info = hosts.get(i).serviceInfo; 1860 tryBindTransport(info); 1861 } 1862 } 1863 1864 // Verify that the service exists and is hosted by a privileged app, then proceed to bind 1865 boolean tryBindTransport(ServiceInfo info) { 1866 try { 1867 PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0); 1868 if ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) { 1869 return bindTransport(info); 1870 } else { 1871 Slog.w(TAG, "Transport package " + info.packageName + " not privileged"); 1872 } 1873 } catch (NameNotFoundException e) { 1874 Slog.w(TAG, "Problem resolving transport package " + info.packageName); 1875 } 1876 return false; 1877 } 1878 1879 // Actually bind; presumes that we have already validated the transport service 1880 boolean bindTransport(ServiceInfo transport) { 1881 ComponentName svcName = new ComponentName(transport.packageName, transport.name); 1882 if (DEBUG) { 1883 Slog.i(TAG, "Binding to transport host " + svcName); 1884 } 1885 Intent intent = new Intent(mTransportServiceIntent); 1886 intent.setComponent(svcName); 1887 1888 TransportConnection connection; 1889 synchronized (mTransports) { 1890 connection = mTransportConnections.get(transport.packageName); 1891 if (null == connection) { 1892 connection = new TransportConnection(transport); 1893 mTransportConnections.put(transport.packageName, connection); 1894 } else { 1895 // This is a rebind due to package upgrade. The service won't be 1896 // automatically relaunched for us until we explicitly rebind, but 1897 // we need to unbind the now-orphaned original connection. 1898 mContext.unbindService(connection); 1899 } 1900 } 1901 return mContext.bindServiceAsUser(intent, 1902 connection, Context.BIND_AUTO_CREATE, 1903 UserHandle.OWNER); 1904 } 1905 1906 // Add the backup agents in the given packages to our set of known backup participants. 1907 // If 'packageNames' is null, adds all backup agents in the whole system. 1908 void addPackageParticipantsLocked(String[] packageNames) { 1909 // Look for apps that define the android:backupAgent attribute 1910 List<PackageInfo> targetApps = allAgentPackages(); 1911 if (packageNames != null) { 1912 if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length); 1913 for (String packageName : packageNames) { 1914 addPackageParticipantsLockedInner(packageName, targetApps); 1915 } 1916 } else { 1917 if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all"); 1918 addPackageParticipantsLockedInner(null, targetApps); 1919 } 1920 } 1921 1922 private void addPackageParticipantsLockedInner(String packageName, 1923 List<PackageInfo> targetPkgs) { 1924 if (MORE_DEBUG) { 1925 Slog.v(TAG, "Examining " + packageName + " for backup agent"); 1926 } 1927 1928 for (PackageInfo pkg : targetPkgs) { 1929 if (packageName == null || pkg.packageName.equals(packageName)) { 1930 int uid = pkg.applicationInfo.uid; 1931 HashSet<String> set = mBackupParticipants.get(uid); 1932 if (set == null) { 1933 set = new HashSet<String>(); 1934 mBackupParticipants.put(uid, set); 1935 } 1936 set.add(pkg.packageName); 1937 if (MORE_DEBUG) Slog.v(TAG, "Agent found; added"); 1938 1939 // Schedule a backup for it on general principles 1940 if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName); 1941 dataChangedImpl(pkg.packageName); 1942 } 1943 } 1944 } 1945 1946 // Remove the given packages' entries from our known active set. 1947 void removePackageParticipantsLocked(String[] packageNames, int oldUid) { 1948 if (packageNames == null) { 1949 Slog.w(TAG, "removePackageParticipants with null list"); 1950 return; 1951 } 1952 1953 if (MORE_DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid 1954 + " #" + packageNames.length); 1955 for (String pkg : packageNames) { 1956 // Known previous UID, so we know which package set to check 1957 HashSet<String> set = mBackupParticipants.get(oldUid); 1958 if (set != null && set.contains(pkg)) { 1959 removePackageFromSetLocked(set, pkg); 1960 if (set.isEmpty()) { 1961 if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set"); 1962 mBackupParticipants.remove(oldUid); 1963 } 1964 } 1965 } 1966 } 1967 1968 private void removePackageFromSetLocked(final HashSet<String> set, 1969 final String packageName) { 1970 if (set.contains(packageName)) { 1971 // Found it. Remove this one package from the bookkeeping, and 1972 // if it's the last participating app under this uid we drop the 1973 // (now-empty) set as well. 1974 // Note that we deliberately leave it 'known' in the "ever backed up" 1975 // bookkeeping so that its current-dataset data will be retrieved 1976 // if the app is subsequently reinstalled 1977 if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName); 1978 set.remove(packageName); 1979 mPendingBackups.remove(packageName); 1980 } 1981 } 1982 1983 // Returns the set of all applications that define an android:backupAgent attribute 1984 List<PackageInfo> allAgentPackages() { 1985 // !!! TODO: cache this and regenerate only when necessary 1986 int flags = PackageManager.GET_SIGNATURES; 1987 List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags); 1988 int N = packages.size(); 1989 for (int a = N-1; a >= 0; a--) { 1990 PackageInfo pkg = packages.get(a); 1991 try { 1992 ApplicationInfo app = pkg.applicationInfo; 1993 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) 1994 || app.backupAgentName == null) { 1995 packages.remove(a); 1996 } 1997 else { 1998 // we will need the shared library path, so look that up and store it here. 1999 // This is used implicitly when we pass the PackageInfo object off to 2000 // the Activity Manager to launch the app for backup/restore purposes. 2001 app = mPackageManager.getApplicationInfo(pkg.packageName, 2002 PackageManager.GET_SHARED_LIBRARY_FILES); 2003 pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles; 2004 } 2005 } catch (NameNotFoundException e) { 2006 packages.remove(a); 2007 } 2008 } 2009 return packages; 2010 } 2011 2012 // Called from the backup tasks: record that the given app has been successfully 2013 // backed up at least once. This includes both key/value and full-data backups 2014 // through the transport. 2015 void logBackupComplete(String packageName) { 2016 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return; 2017 2018 synchronized (mEverStoredApps) { 2019 if (!mEverStoredApps.add(packageName)) return; 2020 2021 RandomAccessFile out = null; 2022 try { 2023 out = new RandomAccessFile(mEverStored, "rws"); 2024 out.seek(out.length()); 2025 out.writeUTF(packageName); 2026 } catch (IOException e) { 2027 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored); 2028 } finally { 2029 try { if (out != null) out.close(); } catch (IOException e) {} 2030 } 2031 } 2032 } 2033 2034 // Remove our awareness of having ever backed up the given package 2035 void removeEverBackedUp(String packageName) { 2036 if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName); 2037 if (MORE_DEBUG) Slog.v(TAG, "New set:"); 2038 2039 synchronized (mEverStoredApps) { 2040 // Rewrite the file and rename to overwrite. If we reboot in the middle, 2041 // we'll recognize on initialization time that the package no longer 2042 // exists and fix it up then. 2043 File tempKnownFile = new File(mBaseStateDir, "processed.new"); 2044 RandomAccessFile known = null; 2045 try { 2046 known = new RandomAccessFile(tempKnownFile, "rws"); 2047 mEverStoredApps.remove(packageName); 2048 for (String s : mEverStoredApps) { 2049 known.writeUTF(s); 2050 if (MORE_DEBUG) Slog.v(TAG, " " + s); 2051 } 2052 known.close(); 2053 known = null; 2054 if (!tempKnownFile.renameTo(mEverStored)) { 2055 throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored); 2056 } 2057 } catch (IOException e) { 2058 // Bad: we couldn't create the new copy. For safety's sake we 2059 // abandon the whole process and remove all what's-backed-up 2060 // state entirely, meaning we'll force a backup pass for every 2061 // participant on the next boot or [re]install. 2062 Slog.w(TAG, "Error rewriting " + mEverStored, e); 2063 mEverStoredApps.clear(); 2064 tempKnownFile.delete(); 2065 mEverStored.delete(); 2066 } finally { 2067 try { if (known != null) known.close(); } catch (IOException e) {} 2068 } 2069 } 2070 } 2071 2072 // Persistently record the current and ancestral backup tokens as well 2073 // as the set of packages with data [supposedly] available in the 2074 // ancestral dataset. 2075 void writeRestoreTokens() { 2076 try { 2077 RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd"); 2078 2079 // First, the version number of this record, for futureproofing 2080 af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION); 2081 2082 // Write the ancestral and current tokens 2083 af.writeLong(mAncestralToken); 2084 af.writeLong(mCurrentToken); 2085 2086 // Now write the set of ancestral packages 2087 if (mAncestralPackages == null) { 2088 af.writeInt(-1); 2089 } else { 2090 af.writeInt(mAncestralPackages.size()); 2091 if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size()); 2092 for (String pkgName : mAncestralPackages) { 2093 af.writeUTF(pkgName); 2094 if (MORE_DEBUG) Slog.v(TAG, " " + pkgName); 2095 } 2096 } 2097 af.close(); 2098 } catch (IOException e) { 2099 Slog.w(TAG, "Unable to write token file:", e); 2100 } 2101 } 2102 2103 // Return the given transport 2104 private IBackupTransport getTransport(String transportName) { 2105 synchronized (mTransports) { 2106 IBackupTransport transport = mTransports.get(transportName); 2107 if (transport == null) { 2108 Slog.w(TAG, "Requested unavailable transport: " + transportName); 2109 } 2110 return transport; 2111 } 2112 } 2113 2114 // fire off a backup agent, blocking until it attaches or times out 2115 IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) { 2116 IBackupAgent agent = null; 2117 synchronized(mAgentConnectLock) { 2118 mConnecting = true; 2119 mConnectedAgent = null; 2120 try { 2121 if (mActivityManager.bindBackupAgent(app, mode)) { 2122 Slog.d(TAG, "awaiting agent for " + app); 2123 2124 // success; wait for the agent to arrive 2125 // only wait 10 seconds for the bind to happen 2126 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; 2127 while (mConnecting && mConnectedAgent == null 2128 && (System.currentTimeMillis() < timeoutMark)) { 2129 try { 2130 mAgentConnectLock.wait(5000); 2131 } catch (InterruptedException e) { 2132 // just bail 2133 if (DEBUG) Slog.w(TAG, "Interrupted: " + e); 2134 mActivityManager.clearPendingBackup(); 2135 return null; 2136 } 2137 } 2138 2139 // if we timed out with no connect, abort and move on 2140 if (mConnecting == true) { 2141 Slog.w(TAG, "Timeout waiting for agent " + app); 2142 mActivityManager.clearPendingBackup(); 2143 return null; 2144 } 2145 if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent); 2146 agent = mConnectedAgent; 2147 } 2148 } catch (RemoteException e) { 2149 // can't happen - ActivityManager is local 2150 } 2151 } 2152 return agent; 2153 } 2154 2155 // clear an application's data, blocking until the operation completes or times out 2156 void clearApplicationDataSynchronous(String packageName) { 2157 // Don't wipe packages marked allowClearUserData=false 2158 try { 2159 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); 2160 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) { 2161 if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping " 2162 + packageName); 2163 return; 2164 } 2165 } catch (NameNotFoundException e) { 2166 Slog.w(TAG, "Tried to clear data for " + packageName + " but not found"); 2167 return; 2168 } 2169 2170 ClearDataObserver observer = new ClearDataObserver(); 2171 2172 synchronized(mClearDataLock) { 2173 mClearingData = true; 2174 try { 2175 mActivityManager.clearApplicationUserData(packageName, observer, 0); 2176 } catch (RemoteException e) { 2177 // can't happen because the activity manager is in this process 2178 } 2179 2180 // only wait 10 seconds for the clear data to happen 2181 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; 2182 while (mClearingData && (System.currentTimeMillis() < timeoutMark)) { 2183 try { 2184 mClearDataLock.wait(5000); 2185 } catch (InterruptedException e) { 2186 // won't happen, but still. 2187 mClearingData = false; 2188 } 2189 } 2190 } 2191 } 2192 2193 class ClearDataObserver extends IPackageDataObserver.Stub { 2194 public void onRemoveCompleted(String packageName, boolean succeeded) { 2195 synchronized(mClearDataLock) { 2196 mClearingData = false; 2197 mClearDataLock.notifyAll(); 2198 } 2199 } 2200 } 2201 2202 // Get the restore-set token for the best-available restore set for this package: 2203 // the active set if possible, else the ancestral one. Returns zero if none available. 2204 long getAvailableRestoreToken(String packageName) { 2205 long token = mAncestralToken; 2206 synchronized (mQueueLock) { 2207 if (mEverStoredApps.contains(packageName)) { 2208 token = mCurrentToken; 2209 } 2210 } 2211 return token; 2212 } 2213 2214 // ----- 2215 // Interface and methods used by the asynchronous-with-timeout backup/restore operations 2216 2217 interface BackupRestoreTask { 2218 // Execute one tick of whatever state machine the task implements 2219 void execute(); 2220 2221 // An operation that wanted a callback has completed 2222 void operationComplete(); 2223 2224 // An operation that wanted a callback has timed out 2225 void handleTimeout(); 2226 } 2227 2228 void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) { 2229 if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token) 2230 + " interval=" + interval); 2231 synchronized (mCurrentOpLock) { 2232 mCurrentOperations.put(token, new Operation(OP_PENDING, callback)); 2233 2234 Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback); 2235 mBackupHandler.sendMessageDelayed(msg, interval); 2236 } 2237 } 2238 2239 // synchronous waiter case 2240 boolean waitUntilOperationComplete(int token) { 2241 if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for " 2242 + Integer.toHexString(token)); 2243 int finalState = OP_PENDING; 2244 Operation op = null; 2245 synchronized (mCurrentOpLock) { 2246 while (true) { 2247 op = mCurrentOperations.get(token); 2248 if (op == null) { 2249 // mysterious disappearance: treat as success with no callback 2250 break; 2251 } else { 2252 if (op.state == OP_PENDING) { 2253 try { 2254 mCurrentOpLock.wait(); 2255 } catch (InterruptedException e) {} 2256 // When the wait is notified we loop around and recheck the current state 2257 } else { 2258 // No longer pending; we're done 2259 finalState = op.state; 2260 break; 2261 } 2262 } 2263 } 2264 } 2265 2266 mBackupHandler.removeMessages(MSG_TIMEOUT); 2267 if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token) 2268 + " complete: finalState=" + finalState); 2269 return finalState == OP_ACKNOWLEDGED; 2270 } 2271 2272 void handleTimeout(int token, Object obj) { 2273 // Notify any synchronous waiters 2274 Operation op = null; 2275 synchronized (mCurrentOpLock) { 2276 op = mCurrentOperations.get(token); 2277 if (MORE_DEBUG) { 2278 if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token) 2279 + " but no op found"); 2280 } 2281 int state = (op != null) ? op.state : OP_TIMEOUT; 2282 if (state == OP_PENDING) { 2283 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token)); 2284 op.state = OP_TIMEOUT; 2285 mCurrentOperations.put(token, op); 2286 } 2287 mCurrentOpLock.notifyAll(); 2288 } 2289 2290 // If there's a TimeoutHandler for this event, call it 2291 if (op != null && op.callback != null) { 2292 op.callback.handleTimeout(); 2293 } 2294 } 2295 2296 // ----- Back up a set of applications via a worker thread ----- 2297 2298 enum BackupState { 2299 INITIAL, 2300 RUNNING_QUEUE, 2301 FINAL 2302 } 2303 2304 class PerformBackupTask implements BackupRestoreTask { 2305 private static final String TAG = "PerformBackupTask"; 2306 2307 IBackupTransport mTransport; 2308 ArrayList<BackupRequest> mQueue; 2309 ArrayList<BackupRequest> mOriginalQueue; 2310 File mStateDir; 2311 File mJournal; 2312 BackupState mCurrentState; 2313 2314 // carried information about the current in-flight operation 2315 IBackupAgent mAgentBinder; 2316 PackageInfo mCurrentPackage; 2317 File mSavedStateName; 2318 File mBackupDataName; 2319 File mNewStateName; 2320 ParcelFileDescriptor mSavedState; 2321 ParcelFileDescriptor mBackupData; 2322 ParcelFileDescriptor mNewState; 2323 int mStatus; 2324 boolean mFinished; 2325 2326 public PerformBackupTask(IBackupTransport transport, String dirName, 2327 ArrayList<BackupRequest> queue, File journal) { 2328 mTransport = transport; 2329 mOriginalQueue = queue; 2330 mJournal = journal; 2331 2332 mStateDir = new File(mBaseStateDir, dirName); 2333 2334 mCurrentState = BackupState.INITIAL; 2335 mFinished = false; 2336 2337 addBackupTrace("STATE => INITIAL"); 2338 } 2339 2340 // Main entry point: perform one chunk of work, updating the state as appropriate 2341 // and reposting the next chunk to the primary backup handler thread. 2342 @Override 2343 public void execute() { 2344 switch (mCurrentState) { 2345 case INITIAL: 2346 beginBackup(); 2347 break; 2348 2349 case RUNNING_QUEUE: 2350 invokeNextAgent(); 2351 break; 2352 2353 case FINAL: 2354 if (!mFinished) finalizeBackup(); 2355 else { 2356 Slog.e(TAG, "Duplicate finish"); 2357 } 2358 mFinished = true; 2359 break; 2360 } 2361 } 2362 2363 // We're starting a backup pass. Initialize the transport and send 2364 // the PM metadata blob if we haven't already. 2365 void beginBackup() { 2366 if (DEBUG_BACKUP_TRACE) { 2367 clearBackupTrace(); 2368 StringBuilder b = new StringBuilder(256); 2369 b.append("beginBackup: ["); 2370 for (BackupRequest req : mOriginalQueue) { 2371 b.append(' '); 2372 b.append(req.packageName); 2373 } 2374 b.append(" ]"); 2375 addBackupTrace(b.toString()); 2376 } 2377 2378 mAgentBinder = null; 2379 mStatus = BackupTransport.TRANSPORT_OK; 2380 2381 // Sanity check: if the queue is empty we have no work to do. 2382 if (mOriginalQueue.isEmpty()) { 2383 Slog.w(TAG, "Backup begun with an empty queue - nothing to do."); 2384 addBackupTrace("queue empty at begin"); 2385 executeNextState(BackupState.FINAL); 2386 return; 2387 } 2388 2389 // We need to retain the original queue contents in case of transport 2390 // failure, but we want a working copy that we can manipulate along 2391 // the way. 2392 mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone(); 2393 2394 if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); 2395 2396 File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); 2397 try { 2398 final String transportName = mTransport.transportDirName(); 2399 EventLog.writeEvent(EventLogTags.BACKUP_START, transportName); 2400 2401 // If we haven't stored package manager metadata yet, we must init the transport. 2402 if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) { 2403 Slog.i(TAG, "Initializing (wiping) backup state and transport storage"); 2404 addBackupTrace("initializing transport " + transportName); 2405 resetBackupState(mStateDir); // Just to make sure. 2406 mStatus = mTransport.initializeDevice(); 2407 2408 addBackupTrace("transport.initializeDevice() == " + mStatus); 2409 if (mStatus == BackupTransport.TRANSPORT_OK) { 2410 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 2411 } else { 2412 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 2413 Slog.e(TAG, "Transport error in initializeDevice()"); 2414 } 2415 } 2416 2417 // The package manager doesn't have a proper <application> etc, but since 2418 // it's running here in the system process we can just set up its agent 2419 // directly and use a synthetic BackupRequest. We always run this pass 2420 // because it's cheap and this way we guarantee that we don't get out of 2421 // step even if we're selecting among various transports at run time. 2422 if (mStatus == BackupTransport.TRANSPORT_OK) { 2423 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( 2424 mPackageManager); 2425 mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL, 2426 IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); 2427 addBackupTrace("PMBA invoke: " + mStatus); 2428 2429 // Because the PMBA is a local instance, it has already executed its 2430 // backup callback and returned. Blow away the lingering (spurious) 2431 // pending timeout message for it. 2432 mBackupHandler.removeMessages(MSG_TIMEOUT); 2433 } 2434 2435 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { 2436 // The backend reports that our dataset has been wiped. Note this in 2437 // the event log; the no-success code below will reset the backup 2438 // state as well. 2439 EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName()); 2440 } 2441 } catch (Exception e) { 2442 Slog.e(TAG, "Error in backup thread", e); 2443 addBackupTrace("Exception in backup thread: " + e); 2444 mStatus = BackupTransport.TRANSPORT_ERROR; 2445 } finally { 2446 // If we've succeeded so far, invokeAgentForBackup() will have run the PM 2447 // metadata and its completion/timeout callback will continue the state 2448 // machine chain. If it failed that won't happen; we handle that now. 2449 addBackupTrace("exiting prelim: " + mStatus); 2450 if (mStatus != BackupTransport.TRANSPORT_OK) { 2451 // if things went wrong at this point, we need to 2452 // restage everything and try again later. 2453 resetBackupState(mStateDir); // Just to make sure. 2454 executeNextState(BackupState.FINAL); 2455 } 2456 } 2457 } 2458 2459 // Transport has been initialized and the PM metadata submitted successfully 2460 // if that was warranted. Now we process the single next thing in the queue. 2461 void invokeNextAgent() { 2462 mStatus = BackupTransport.TRANSPORT_OK; 2463 addBackupTrace("invoke q=" + mQueue.size()); 2464 2465 // Sanity check that we have work to do. If not, skip to the end where 2466 // we reestablish the wakelock invariants etc. 2467 if (mQueue.isEmpty()) { 2468 if (DEBUG) Slog.i(TAG, "queue now empty"); 2469 executeNextState(BackupState.FINAL); 2470 return; 2471 } 2472 2473 // pop the entry we're going to process on this step 2474 BackupRequest request = mQueue.get(0); 2475 mQueue.remove(0); 2476 2477 Slog.d(TAG, "starting agent for backup of " + request); 2478 addBackupTrace("launch agent for " + request.packageName); 2479 2480 // Verify that the requested app exists; it might be something that 2481 // requested a backup but was then uninstalled. The request was 2482 // journalled and rather than tamper with the journal it's safer 2483 // to sanity-check here. This also gives us the classname of the 2484 // package's backup agent. 2485 try { 2486 mCurrentPackage = mPackageManager.getPackageInfo(request.packageName, 2487 PackageManager.GET_SIGNATURES); 2488 if (mCurrentPackage.applicationInfo.backupAgentName == null) { 2489 // The manifest has changed but we had a stale backup request pending. 2490 // This won't happen again because the app won't be requesting further 2491 // backups. 2492 Slog.i(TAG, "Package " + request.packageName 2493 + " no longer supports backup; skipping"); 2494 addBackupTrace("skipping - no agent, completion is noop"); 2495 executeNextState(BackupState.RUNNING_QUEUE); 2496 return; 2497 } 2498 2499 if ((mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) { 2500 // The app has been force-stopped or cleared or just installed, 2501 // and not yet launched out of that state, so just as it won't 2502 // receive broadcasts, we won't run it for backup. 2503 addBackupTrace("skipping - stopped"); 2504 executeNextState(BackupState.RUNNING_QUEUE); 2505 return; 2506 } 2507 2508 IBackupAgent agent = null; 2509 try { 2510 mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid)); 2511 agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo, 2512 IApplicationThread.BACKUP_MODE_INCREMENTAL); 2513 addBackupTrace("agent bound; a? = " + (agent != null)); 2514 if (agent != null) { 2515 mAgentBinder = agent; 2516 mStatus = invokeAgentForBackup(request.packageName, agent, mTransport); 2517 // at this point we'll either get a completion callback from the 2518 // agent, or a timeout message on the main handler. either way, we're 2519 // done here as long as we're successful so far. 2520 } else { 2521 // Timeout waiting for the agent 2522 mStatus = BackupTransport.AGENT_ERROR; 2523 } 2524 } catch (SecurityException ex) { 2525 // Try for the next one. 2526 Slog.d(TAG, "error in bind/backup", ex); 2527 mStatus = BackupTransport.AGENT_ERROR; 2528 addBackupTrace("agent SE"); 2529 } 2530 } catch (NameNotFoundException e) { 2531 Slog.d(TAG, "Package does not exist; skipping"); 2532 addBackupTrace("no such package"); 2533 mStatus = BackupTransport.AGENT_UNKNOWN; 2534 } finally { 2535 mWakelock.setWorkSource(null); 2536 2537 // If there was an agent error, no timeout/completion handling will occur. 2538 // That means we need to direct to the next state ourselves. 2539 if (mStatus != BackupTransport.TRANSPORT_OK) { 2540 BackupState nextState = BackupState.RUNNING_QUEUE; 2541 mAgentBinder = null; 2542 2543 // An agent-level failure means we reenqueue this one agent for 2544 // a later retry, but otherwise proceed normally. 2545 if (mStatus == BackupTransport.AGENT_ERROR) { 2546 if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName 2547 + " - restaging"); 2548 dataChangedImpl(request.packageName); 2549 mStatus = BackupTransport.TRANSPORT_OK; 2550 if (mQueue.isEmpty()) nextState = BackupState.FINAL; 2551 } else if (mStatus == BackupTransport.AGENT_UNKNOWN) { 2552 // Failed lookup of the app, so we couldn't bring up an agent, but 2553 // we're otherwise fine. Just drop it and go on to the next as usual. 2554 mStatus = BackupTransport.TRANSPORT_OK; 2555 } else { 2556 // Transport-level failure means we reenqueue everything 2557 revertAndEndBackup(); 2558 nextState = BackupState.FINAL; 2559 } 2560 2561 executeNextState(nextState); 2562 } else { 2563 // success case 2564 addBackupTrace("expecting completion/timeout callback"); 2565 } 2566 } 2567 } 2568 2569 void finalizeBackup() { 2570 addBackupTrace("finishing"); 2571 2572 // Either backup was successful, in which case we of course do not need 2573 // this pass's journal any more; or it failed, in which case we just 2574 // re-enqueued all of these packages in the current active journal. 2575 // Either way, we no longer need this pass's journal. 2576 if (mJournal != null && !mJournal.delete()) { 2577 Slog.e(TAG, "Unable to remove backup journal file " + mJournal); 2578 } 2579 2580 // If everything actually went through and this is the first time we've 2581 // done a backup, we can now record what the current backup dataset token 2582 // is. 2583 if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) { 2584 addBackupTrace("success; recording token"); 2585 try { 2586 mCurrentToken = mTransport.getCurrentRestoreSet(); 2587 writeRestoreTokens(); 2588 } catch (RemoteException e) { 2589 // nothing for it at this point, unfortunately, but this will be 2590 // recorded the next time we fully succeed. 2591 addBackupTrace("transport threw returning token"); 2592 } 2593 } 2594 2595 // Set up the next backup pass - at this point we can set mBackupRunning 2596 // to false to allow another pass to fire, because we're done with the 2597 // state machine sequence and the wakelock is refcounted. 2598 synchronized (mQueueLock) { 2599 mBackupRunning = false; 2600 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { 2601 // Make sure we back up everything and perform the one-time init 2602 clearMetadata(); 2603 if (DEBUG) Slog.d(TAG, "Server requires init; rerunning"); 2604 addBackupTrace("init required; rerunning"); 2605 backupNow(); 2606 } 2607 } 2608 2609 // Only once we're entirely finished do we release the wakelock 2610 clearBackupTrace(); 2611 Slog.i(BackupManagerService.TAG, "Backup pass finished."); 2612 mWakelock.release(); 2613 } 2614 2615 // Remove the PM metadata state. This will generate an init on the next pass. 2616 void clearMetadata() { 2617 final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL); 2618 if (pmState.exists()) pmState.delete(); 2619 } 2620 2621 // Invoke an agent's doBackup() and start a timeout message spinning on the main 2622 // handler in case it doesn't get back to us. 2623 int invokeAgentForBackup(String packageName, IBackupAgent agent, 2624 IBackupTransport transport) { 2625 if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName); 2626 addBackupTrace("invoking " + packageName); 2627 2628 mSavedStateName = new File(mStateDir, packageName); 2629 mBackupDataName = new File(mDataDir, packageName + ".data"); 2630 mNewStateName = new File(mStateDir, packageName + ".new"); 2631 if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName); 2632 2633 mSavedState = null; 2634 mBackupData = null; 2635 mNewState = null; 2636 2637 final int token = generateToken(); 2638 try { 2639 // Look up the package info & signatures. This is first so that if it 2640 // throws an exception, there's no file setup yet that would need to 2641 // be unraveled. 2642 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) { 2643 // The metadata 'package' is synthetic; construct one and make 2644 // sure our global state is pointed at it 2645 mCurrentPackage = new PackageInfo(); 2646 mCurrentPackage.packageName = packageName; 2647 } 2648 2649 // In a full backup, we pass a null ParcelFileDescriptor as 2650 // the saved-state "file". This is by definition an incremental, 2651 // so we build a saved state file to pass. 2652 mSavedState = ParcelFileDescriptor.open(mSavedStateName, 2653 ParcelFileDescriptor.MODE_READ_ONLY | 2654 ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary 2655 2656 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 2657 ParcelFileDescriptor.MODE_READ_WRITE | 2658 ParcelFileDescriptor.MODE_CREATE | 2659 ParcelFileDescriptor.MODE_TRUNCATE); 2660 2661 if (!SELinux.restorecon(mBackupDataName)) { 2662 Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName); 2663 } 2664 2665 mNewState = ParcelFileDescriptor.open(mNewStateName, 2666 ParcelFileDescriptor.MODE_READ_WRITE | 2667 ParcelFileDescriptor.MODE_CREATE | 2668 ParcelFileDescriptor.MODE_TRUNCATE); 2669 2670 // Initiate the target's backup pass 2671 addBackupTrace("setting timeout"); 2672 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this); 2673 addBackupTrace("calling agent doBackup()"); 2674 agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder); 2675 } catch (Exception e) { 2676 Slog.e(TAG, "Error invoking for backup on " + packageName); 2677 addBackupTrace("exception: " + e); 2678 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, 2679 e.toString()); 2680 agentErrorCleanup(); 2681 return BackupTransport.AGENT_ERROR; 2682 } 2683 2684 // At this point the agent is off and running. The next thing to happen will 2685 // either be a callback from the agent, at which point we'll process its data 2686 // for transport, or a timeout. Either way the next phase will happen in 2687 // response to the TimeoutHandler interface callbacks. 2688 addBackupTrace("invoke success"); 2689 return BackupTransport.TRANSPORT_OK; 2690 } 2691 2692 public void failAgent(IBackupAgent agent, String message) { 2693 try { 2694 agent.fail(message); 2695 } catch (Exception e) { 2696 Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName); 2697 } 2698 } 2699 2700 // SHA-1 a byte array and return the result in hex 2701 private String SHA1Checksum(byte[] input) { 2702 final byte[] checksum; 2703 try { 2704 MessageDigest md = MessageDigest.getInstance("SHA-1"); 2705 checksum = md.digest(input); 2706 } catch (NoSuchAlgorithmException e) { 2707 Slog.e(TAG, "Unable to use SHA-1!"); 2708 return "00"; 2709 } 2710 2711 StringBuffer sb = new StringBuffer(checksum.length * 2); 2712 for (int i = 0; i < checksum.length; i++) { 2713 sb.append(Integer.toHexString(checksum[i])); 2714 } 2715 return sb.toString(); 2716 } 2717 2718 private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName) 2719 throws IOException { 2720 byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName, 2721 UserHandle.USER_OWNER); 2722 // has the widget state changed since last time? 2723 final File widgetFile = new File(mStateDir, pkgName + "_widget"); 2724 final boolean priorStateExists = widgetFile.exists(); 2725 2726 if (MORE_DEBUG) { 2727 if (priorStateExists || widgetState != null) { 2728 Slog.i(TAG, "Checking widget update: state=" + (widgetState != null) 2729 + " prior=" + priorStateExists); 2730 } 2731 } 2732 2733 if (!priorStateExists && widgetState == null) { 2734 // no prior state, no new state => nothing to do 2735 return; 2736 } 2737 2738 // if the new state is not null, we might need to compare checksums to 2739 // determine whether to update the widget blob in the archive. If the 2740 // widget state *is* null, we know a priori at this point that we simply 2741 // need to commit a deletion for it. 2742 String newChecksum = null; 2743 if (widgetState != null) { 2744 newChecksum = SHA1Checksum(widgetState); 2745 if (priorStateExists) { 2746 final String priorChecksum; 2747 try ( 2748 FileInputStream fin = new FileInputStream(widgetFile); 2749 DataInputStream in = new DataInputStream(fin) 2750 ) { 2751 priorChecksum = in.readUTF(); 2752 } 2753 if (Objects.equals(newChecksum, priorChecksum)) { 2754 // Same checksum => no state change => don't rewrite the widget data 2755 return; 2756 } 2757 } 2758 } // else widget state *became* empty, so we need to commit a deletion 2759 2760 BackupDataOutput out = new BackupDataOutput(fd); 2761 if (widgetState != null) { 2762 try ( 2763 FileOutputStream fout = new FileOutputStream(widgetFile); 2764 DataOutputStream stateOut = new DataOutputStream(fout) 2765 ) { 2766 stateOut.writeUTF(newChecksum); 2767 } 2768 2769 out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length); 2770 out.writeEntityData(widgetState, widgetState.length); 2771 } else { 2772 // Widget state for this app has been removed; commit a deletion 2773 out.writeEntityHeader(KEY_WIDGET_STATE, -1); 2774 widgetFile.delete(); 2775 } 2776 } 2777 2778 @Override 2779 public void operationComplete() { 2780 // Okay, the agent successfully reported back to us! 2781 final String pkgName = mCurrentPackage.packageName; 2782 final long filepos = mBackupDataName.length(); 2783 FileDescriptor fd = mBackupData.getFileDescriptor(); 2784 try { 2785 // If it's a 3rd party app, see whether they wrote any protected keys 2786 // and complain mightily if they are attempting shenanigans. 2787 if (mCurrentPackage.applicationInfo != null && 2788 (mCurrentPackage.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) { 2789 ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName, 2790 ParcelFileDescriptor.MODE_READ_ONLY); 2791 BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor()); 2792 try { 2793 while (in.readNextHeader()) { 2794 final String key = in.getKey(); 2795 if (key != null && key.charAt(0) >= 0xff00) { 2796 // Not okay: crash them and bail. 2797 failAgent(mAgentBinder, "Illegal backup key: " + key); 2798 addBackupTrace("illegal key " + key + " from " + pkgName); 2799 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName, 2800 "bad key"); 2801 mBackupHandler.removeMessages(MSG_TIMEOUT); 2802 agentErrorCleanup(); 2803 // agentErrorCleanup() implicitly executes next state properly 2804 return; 2805 } 2806 in.skipEntityData(); 2807 } 2808 } finally { 2809 if (readFd != null) { 2810 readFd.close(); 2811 } 2812 } 2813 } 2814 2815 // Piggyback the widget state payload, if any 2816 writeWidgetPayloadIfAppropriate(fd, pkgName); 2817 } catch (IOException e) { 2818 // Hard disk error; recovery/failure policy TBD. For now roll back, 2819 // but we may want to consider this a transport-level failure (i.e. 2820 // we're in such a bad state that we can't contemplate doing backup 2821 // operations any more during this pass). 2822 Slog.w(TAG, "Unable to save widget state for " + pkgName); 2823 try { 2824 Os.ftruncate(fd, filepos); 2825 } catch (ErrnoException ee) { 2826 Slog.w(TAG, "Unable to roll back!"); 2827 } 2828 } 2829 2830 // Spin the data off to the transport and proceed with the next stage. 2831 if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for " 2832 + pkgName); 2833 mBackupHandler.removeMessages(MSG_TIMEOUT); 2834 clearAgentState(); 2835 addBackupTrace("operation complete"); 2836 2837 ParcelFileDescriptor backupData = null; 2838 mStatus = BackupTransport.TRANSPORT_OK; 2839 try { 2840 int size = (int) mBackupDataName.length(); 2841 if (size > 0) { 2842 if (mStatus == BackupTransport.TRANSPORT_OK) { 2843 backupData = ParcelFileDescriptor.open(mBackupDataName, 2844 ParcelFileDescriptor.MODE_READ_ONLY); 2845 addBackupTrace("sending data to transport"); 2846 mStatus = mTransport.performBackup(mCurrentPackage, backupData); 2847 } 2848 2849 // TODO - We call finishBackup() for each application backed up, because 2850 // we need to know now whether it succeeded or failed. Instead, we should 2851 // hold off on finishBackup() until the end, which implies holding off on 2852 // renaming *all* the output state files (see below) until that happens. 2853 2854 addBackupTrace("data delivered: " + mStatus); 2855 if (mStatus == BackupTransport.TRANSPORT_OK) { 2856 addBackupTrace("finishing op on transport"); 2857 mStatus = mTransport.finishBackup(); 2858 addBackupTrace("finished: " + mStatus); 2859 } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 2860 addBackupTrace("transport rejected package"); 2861 } 2862 } else { 2863 if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport"); 2864 addBackupTrace("no data to send"); 2865 } 2866 2867 if (mStatus == BackupTransport.TRANSPORT_OK) { 2868 // After successful transport, delete the now-stale data 2869 // and juggle the files so that next time we supply the agent 2870 // with the new state file it just created. 2871 mBackupDataName.delete(); 2872 mNewStateName.renameTo(mSavedStateName); 2873 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size); 2874 logBackupComplete(pkgName); 2875 } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 2876 // The transport has rejected backup of this specific package. Roll it 2877 // back but proceed with running the rest of the queue. 2878 mBackupDataName.delete(); 2879 mNewStateName.delete(); 2880 EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected"); 2881 } else { 2882 // Actual transport-level failure to communicate the data to the backend 2883 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); 2884 } 2885 } catch (Exception e) { 2886 Slog.e(TAG, "Transport error backing up " + pkgName, e); 2887 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); 2888 mStatus = BackupTransport.TRANSPORT_ERROR; 2889 } finally { 2890 try { if (backupData != null) backupData.close(); } catch (IOException e) {} 2891 } 2892 2893 final BackupState nextState; 2894 if (mStatus == BackupTransport.TRANSPORT_OK 2895 || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 2896 // Success or single-package rejection. Proceed with the next app if any, 2897 // otherwise we're done. 2898 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE; 2899 } else { 2900 // Any other error here indicates a transport-level failure. That means 2901 // we need to halt everything and reschedule everything for next time. 2902 revertAndEndBackup(); 2903 nextState = BackupState.FINAL; 2904 } 2905 2906 executeNextState(nextState); 2907 } 2908 2909 @Override 2910 public void handleTimeout() { 2911 // Whoops, the current agent timed out running doBackup(). Tidy up and restage 2912 // it for the next time we run a backup pass. 2913 // !!! TODO: keep track of failure counts per agent, and blacklist those which 2914 // fail repeatedly (i.e. have proved themselves to be buggy). 2915 Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName); 2916 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName, 2917 "timeout"); 2918 addBackupTrace("timeout of " + mCurrentPackage.packageName); 2919 agentErrorCleanup(); 2920 dataChangedImpl(mCurrentPackage.packageName); 2921 } 2922 2923 void revertAndEndBackup() { 2924 if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything"); 2925 addBackupTrace("transport error; reverting"); 2926 for (BackupRequest request : mOriginalQueue) { 2927 dataChangedImpl(request.packageName); 2928 } 2929 // We also want to reset the backup schedule based on whatever 2930 // the transport suggests by way of retry/backoff time. 2931 restartBackupAlarm(); 2932 } 2933 2934 void agentErrorCleanup() { 2935 mBackupDataName.delete(); 2936 mNewStateName.delete(); 2937 clearAgentState(); 2938 2939 executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE); 2940 } 2941 2942 // Cleanup common to both success and failure cases 2943 void clearAgentState() { 2944 try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {} 2945 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 2946 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 2947 synchronized (mCurrentOpLock) { 2948 // Current-operation callback handling requires the validity of these various 2949 // bits of internal state as an invariant of the operation still being live. 2950 // This means we make sure to clear all of the state in unison inside the lock. 2951 mCurrentOperations.clear(); 2952 mSavedState = mBackupData = mNewState = null; 2953 } 2954 2955 // If this was a pseudopackage there's no associated Activity Manager state 2956 if (mCurrentPackage.applicationInfo != null) { 2957 addBackupTrace("unbinding " + mCurrentPackage.packageName); 2958 try { // unbind even on timeout, just in case 2959 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 2960 } catch (RemoteException e) { /* can't happen; activity manager is local */ } 2961 } 2962 } 2963 2964 void restartBackupAlarm() { 2965 addBackupTrace("setting backup trigger"); 2966 synchronized (mQueueLock) { 2967 try { 2968 startBackupAlarmsLocked(mTransport.requestBackupTime()); 2969 } catch (RemoteException e) { /* cannot happen */ } 2970 } 2971 } 2972 2973 void executeNextState(BackupState nextState) { 2974 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 2975 + this + " nextState=" + nextState); 2976 addBackupTrace("executeNextState => " + nextState); 2977 mCurrentState = nextState; 2978 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 2979 mBackupHandler.sendMessage(msg); 2980 } 2981 } 2982 2983 2984 // ----- Full backup/restore to a file/socket ----- 2985 2986 class FullBackupObbConnection implements ServiceConnection { 2987 volatile IObbBackupService mService; 2988 2989 FullBackupObbConnection() { 2990 mService = null; 2991 } 2992 2993 public void establish() { 2994 if (DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this); 2995 Intent obbIntent = new Intent().setComponent(new ComponentName( 2996 "com.android.sharedstoragebackup", 2997 "com.android.sharedstoragebackup.ObbBackupService")); 2998 BackupManagerService.this.mContext.bindService( 2999 obbIntent, this, Context.BIND_AUTO_CREATE); 3000 } 3001 3002 public void tearDown() { 3003 BackupManagerService.this.mContext.unbindService(this); 3004 } 3005 3006 public boolean backupObbs(PackageInfo pkg, OutputStream out) { 3007 boolean success = false; 3008 waitForConnection(); 3009 3010 ParcelFileDescriptor[] pipes = null; 3011 try { 3012 pipes = ParcelFileDescriptor.createPipe(); 3013 int token = generateToken(); 3014 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 3015 mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder); 3016 routeSocketDataToOutput(pipes[0], out); 3017 success = waitUntilOperationComplete(token); 3018 } catch (Exception e) { 3019 Slog.w(TAG, "Unable to back up OBBs for " + pkg, e); 3020 } finally { 3021 try { 3022 out.flush(); 3023 if (pipes != null) { 3024 if (pipes[0] != null) pipes[0].close(); 3025 if (pipes[1] != null) pipes[1].close(); 3026 } 3027 } catch (IOException e) { 3028 Slog.w(TAG, "I/O error closing down OBB backup", e); 3029 } 3030 } 3031 return success; 3032 } 3033 3034 public void restoreObbFile(String pkgName, ParcelFileDescriptor data, 3035 long fileSize, int type, String path, long mode, long mtime, 3036 int token, IBackupManager callbackBinder) { 3037 waitForConnection(); 3038 3039 try { 3040 mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime, 3041 token, callbackBinder); 3042 } catch (Exception e) { 3043 Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e); 3044 } 3045 } 3046 3047 private void waitForConnection() { 3048 synchronized (this) { 3049 while (mService == null) { 3050 if (DEBUG) Slog.i(TAG, "...waiting for OBB service binding..."); 3051 try { 3052 this.wait(); 3053 } catch (InterruptedException e) { /* never interrupted */ } 3054 } 3055 if (DEBUG) Slog.i(TAG, "Connected to OBB service; continuing"); 3056 } 3057 } 3058 3059 @Override 3060 public void onServiceConnected(ComponentName name, IBinder service) { 3061 synchronized (this) { 3062 mService = IObbBackupService.Stub.asInterface(service); 3063 if (DEBUG) Slog.i(TAG, "OBB service connection " + mService 3064 + " connected on " + this); 3065 this.notifyAll(); 3066 } 3067 } 3068 3069 @Override 3070 public void onServiceDisconnected(ComponentName name) { 3071 synchronized (this) { 3072 mService = null; 3073 if (DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this); 3074 this.notifyAll(); 3075 } 3076 } 3077 3078 } 3079 3080 private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out) 3081 throws IOException { 3082 FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor()); 3083 DataInputStream in = new DataInputStream(raw); 3084 3085 byte[] buffer = new byte[32 * 1024]; 3086 int chunkTotal; 3087 while ((chunkTotal = in.readInt()) > 0) { 3088 while (chunkTotal > 0) { 3089 int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal; 3090 int nRead = in.read(buffer, 0, toRead); 3091 out.write(buffer, 0, nRead); 3092 chunkTotal -= nRead; 3093 } 3094 } 3095 } 3096 3097 // Core logic for performing one package's full backup, gathering the tarball from the 3098 // application and emitting it to the designated OutputStream. 3099 class FullBackupEngine { 3100 OutputStream mOutput; 3101 IFullBackupRestoreObserver mObserver; 3102 File mFilesDir; 3103 File mManifestFile; 3104 File mMetadataFile; 3105 boolean mIncludeApks; 3106 3107 class FullBackupRunner implements Runnable { 3108 PackageInfo mPackage; 3109 byte[] mWidgetData; 3110 IBackupAgent mAgent; 3111 ParcelFileDescriptor mPipe; 3112 int mToken; 3113 boolean mSendApk; 3114 boolean mWriteManifest; 3115 3116 FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe, 3117 int token, boolean sendApk, boolean writeManifest, byte[] widgetData) 3118 throws IOException { 3119 mPackage = pack; 3120 mWidgetData = widgetData; 3121 mAgent = agent; 3122 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor()); 3123 mToken = token; 3124 mSendApk = sendApk; 3125 mWriteManifest = writeManifest; 3126 } 3127 3128 @Override 3129 public void run() { 3130 try { 3131 BackupDataOutput output = new BackupDataOutput( 3132 mPipe.getFileDescriptor()); 3133 3134 if (mWriteManifest) { 3135 final boolean writeWidgetData = mWidgetData != null; 3136 if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName); 3137 writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData); 3138 FullBackup.backupToTar(mPackage.packageName, null, null, 3139 mFilesDir.getAbsolutePath(), 3140 mManifestFile.getAbsolutePath(), 3141 output); 3142 mManifestFile.delete(); 3143 3144 // We only need to write a metadata file if we have widget data to stash 3145 if (writeWidgetData) { 3146 writeMetadata(mPackage, mMetadataFile, mWidgetData); 3147 FullBackup.backupToTar(mPackage.packageName, null, null, 3148 mFilesDir.getAbsolutePath(), 3149 mMetadataFile.getAbsolutePath(), 3150 output); 3151 mMetadataFile.delete(); 3152 } 3153 } 3154 3155 if (mSendApk) { 3156 writeApkToBackup(mPackage, output); 3157 } 3158 3159 if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName); 3160 prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null); 3161 mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder); 3162 } catch (IOException e) { 3163 Slog.e(TAG, "Error running full backup for " + mPackage.packageName); 3164 } catch (RemoteException e) { 3165 Slog.e(TAG, "Remote agent vanished during full backup of " 3166 +