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