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