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