BackupManagerService.java revision d746057f2414cba2bdc69257cc5be8cb681bb592
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 // Done -- how did it turn out? 3539 if (result == BackupTransport.TRANSPORT_OK){ 3540 result = transport.finishBackup(); 3541 } else { 3542 Slog.w(TAG, "Error backing up " + target.packageName); 3543 } 3544 } else if (result == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 3545 if (DEBUG) { 3546 Slog.i(TAG, "Transport rejected backup of " + target.packageName 3547 + ", skipping"); 3548 } 3549 // do nothing, clean up, and continue looping 3550 } else { 3551 if (DEBUG) { 3552 Slog.i(TAG, "Transport failed; aborting backup"); 3553 return; 3554 } 3555 } 3556 cleanUpPipes(transportPipes); 3557 cleanUpPipes(enginePipes); 3558 } 3559 3560 if (DEBUG) { 3561 Slog.i(TAG, "Full backup completed."); 3562 } 3563 } 3564 } catch (Exception e) { 3565 Slog.w(TAG, "Exception trying full transport backup", e); 3566 } finally { 3567 cleanUpPipes(transportPipes); 3568 cleanUpPipes(enginePipes); 3569 synchronized (mLatch) { 3570 mLatch.set(true); 3571 mLatch.notifyAll(); 3572 } 3573 } 3574 } 3575 3576 void cleanUpPipes(ParcelFileDescriptor[] pipes) { 3577 if (pipes != null) { 3578 if (pipes[0] != null) { 3579 ParcelFileDescriptor fd = pipes[0]; 3580 pipes[0] = null; 3581 try { 3582 fd.close(); 3583 } catch (IOException e) { 3584 Slog.w(TAG, "Unable to close pipe!"); 3585 } 3586 } 3587 if (pipes[1] != null) { 3588 ParcelFileDescriptor fd = pipes[1]; 3589 pipes[1] = null; 3590 try { 3591 fd.close(); 3592 } catch (IOException e) { 3593 Slog.w(TAG, "Unable to close pipe!"); 3594 } 3595 } 3596 } 3597 } 3598 3599 // Run the backup and pipe it back to the given socket -- expects to run on 3600 // a standalone thread. The runner owns this half of the pipe, and closes 3601 // it to indicate EOD to the other end. 3602 class SinglePackageBackupRunner implements Runnable { 3603 final ParcelFileDescriptor mOutput; 3604 final PackageInfo mTarget; 3605 final AtomicBoolean mLatch; 3606 3607 SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target, 3608 AtomicBoolean latch) throws IOException { 3609 int oldfd = output.getFd(); 3610 mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor()); 3611 mTarget = target; 3612 mLatch = latch; 3613 } 3614 3615 @Override 3616 public void run() { 3617 try { 3618 FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor()); 3619 FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName, false); 3620 engine.backupOnePackage(mTarget); 3621 } catch (Exception e) { 3622 Slog.e(TAG, "Exception during full package backup of " + mTarget); 3623 } finally { 3624 synchronized (mLatch) { 3625 mLatch.set(true); 3626 mLatch.notifyAll(); 3627 } 3628 try { 3629 mOutput.close(); 3630 } catch (IOException e) { 3631 Slog.w(TAG, "Error closing transport pipe in runner"); 3632 } 3633 } 3634 } 3635 3636 } 3637 } 3638 3639 // ----- Restore infrastructure ----- 3640 3641 abstract class RestoreEngine { 3642 static final String TAG = "RestoreEngine"; 3643 3644 public static final int SUCCESS = 0; 3645 public static final int TARGET_FAILURE = -2; 3646 public static final int TRANSPORT_FAILURE = -3; 3647 3648 private AtomicBoolean mRunning = new AtomicBoolean(false); 3649 private AtomicInteger mResult = new AtomicInteger(SUCCESS); 3650 3651 public boolean isRunning() { 3652 return mRunning.get(); 3653 } 3654 3655 public void setRunning(boolean stillRunning) { 3656 synchronized (mRunning) { 3657 mRunning.set(stillRunning); 3658 mRunning.notifyAll(); 3659 } 3660 } 3661 3662 public int waitForResult() { 3663 synchronized (mRunning) { 3664 while (isRunning()) { 3665 try { 3666 mRunning.wait(); 3667 } catch (InterruptedException e) {} 3668 } 3669 } 3670 return getResult(); 3671 } 3672 3673 public int getResult() { 3674 return mResult.get(); 3675 } 3676 3677 public void setResult(int result) { 3678 mResult.set(result); 3679 } 3680 3681 // TODO: abstract restore state and APIs 3682 } 3683 3684 // ----- Full restore from a file/socket ----- 3685 3686 // Description of a file in the restore datastream 3687 static class FileMetadata { 3688 String packageName; // name of the owning app 3689 String installerPackageName; // name of the market-type app that installed the owner 3690 int type; // e.g. BackupAgent.TYPE_DIRECTORY 3691 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN 3692 String path; // subpath within the semantic domain 3693 long mode; // e.g. 0666 (actually int) 3694 long mtime; // last mod time, UTC time_t (actually int) 3695 long size; // bytes of content 3696 3697 @Override 3698 public String toString() { 3699 StringBuilder sb = new StringBuilder(128); 3700 sb.append("FileMetadata{"); 3701 sb.append(packageName); sb.append(','); 3702 sb.append(type); sb.append(','); 3703 sb.append(domain); sb.append(':'); sb.append(path); sb.append(','); 3704 sb.append(size); 3705 sb.append('}'); 3706 return sb.toString(); 3707 } 3708 } 3709 3710 enum RestorePolicy { 3711 IGNORE, 3712 ACCEPT, 3713 ACCEPT_IF_APK 3714 } 3715 3716 // Full restore engine, used by both adb restore and transport-based full restore 3717 class FullRestoreEngine extends RestoreEngine { 3718 // Dedicated observer, if any 3719 IFullBackupRestoreObserver mObserver; 3720 3721 // Where we're delivering the file data as we go 3722 IBackupAgent mAgent; 3723 3724 // Are we permitted to only deliver a specific package's metadata? 3725 PackageInfo mOnlyPackage; 3726 3727 boolean mAllowApks; 3728 boolean mAllowObbs; 3729 3730 // Which package are we currently handling data for? 3731 String mAgentPackage; 3732 3733 // Info for working with the target app process 3734 ApplicationInfo mTargetApp; 3735 3736 // Machinery for restoring OBBs 3737 FullBackupObbConnection mObbConnection = null; 3738 3739 // possible handling states for a given package in the restore dataset 3740 final HashMap<String, RestorePolicy> mPackagePolicies 3741 = new HashMap<String, RestorePolicy>(); 3742 3743 // installer package names for each encountered app, derived from the manifests 3744 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 3745 3746 // Signatures for a given package found in its manifest file 3747 final HashMap<String, Signature[]> mManifestSignatures 3748 = new HashMap<String, Signature[]>(); 3749 3750 // Packages we've already wiped data on when restoring their first file 3751 final HashSet<String> mClearedPackages = new HashSet<String>(); 3752 3753 // How much data have we moved? 3754 long mBytes; 3755 3756 // Working buffer 3757 byte[] mBuffer; 3758 3759 // Pipes for moving data 3760 ParcelFileDescriptor[] mPipes = null; 3761 3762 // Widget blob to be restored out-of-band 3763 byte[] mWidgetData = null; 3764 3765 // Runner that can be placed in a separate thread to do in-process 3766 // invocations of the full restore API asynchronously 3767 class RestoreFileRunnable implements Runnable { 3768 IBackupAgent mAgent; 3769 FileMetadata mInfo; 3770 ParcelFileDescriptor mSocket; 3771 int mToken; 3772 3773 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 3774 ParcelFileDescriptor socket, int token) throws IOException { 3775 mAgent = agent; 3776 mInfo = info; 3777 mToken = token; 3778 3779 // This class is used strictly for process-local binder invocations. The 3780 // semantics of ParcelFileDescriptor differ in this case; in particular, we 3781 // do not automatically get a 'dup'ed descriptor that we can can continue 3782 // to use asynchronously from the caller. So, we make sure to dup it ourselves 3783 // before proceeding to do the restore. 3784 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 3785 } 3786 3787 @Override 3788 public void run() { 3789 try { 3790 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 3791 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 3792 mToken, mBackupManagerBinder); 3793 } catch (RemoteException e) { 3794 // never happens; this is used strictly for local binder calls 3795 } 3796 } 3797 } 3798 3799 public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage, 3800 boolean allowApks, boolean allowObbs) { 3801 mObserver = observer; 3802 mOnlyPackage = onlyPackage; 3803 mAllowApks = allowApks; 3804 mAllowObbs = allowObbs; 3805 mBuffer = new byte[32 * 1024]; 3806 mBytes = 0; 3807 } 3808 3809 public boolean restoreOneFile(InputStream instream) { 3810 if (!isRunning()) { 3811 Slog.w(TAG, "Restore engine used after halting"); 3812 return false; 3813 } 3814 3815 FileMetadata info; 3816 try { 3817 if (MORE_DEBUG) { 3818 Slog.v(TAG, "Reading tar header for restoring file"); 3819 } 3820 info = readTarHeaders(instream); 3821 if (info != null) { 3822 if (MORE_DEBUG) { 3823 dumpFileMetadata(info); 3824 } 3825 3826 final String pkg = info.packageName; 3827 if (!pkg.equals(mAgentPackage)) { 3828 // In the single-package case, it's a semantic error to expect 3829 // one app's data but see a different app's on the wire 3830 if (mOnlyPackage != null) { 3831 if (!pkg.equals(mOnlyPackage.packageName)) { 3832 Slog.w(TAG, "Expected data for " + mOnlyPackage 3833 + " but saw " + pkg); 3834 setResult(RestoreEngine.TRANSPORT_FAILURE); 3835 setRunning(false); 3836 return false; 3837 } 3838 } 3839 3840 // okay, change in package; set up our various 3841 // bookkeeping if we haven't seen it yet 3842 if (!mPackagePolicies.containsKey(pkg)) { 3843 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 3844 } 3845 3846 // Clean up the previous agent relationship if necessary, 3847 // and let the observer know we're considering a new app. 3848 if (mAgent != null) { 3849 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 3850 // Now we're really done 3851 tearDownPipes(); 3852 tearDownAgent(mTargetApp); 3853 mTargetApp = null; 3854 mAgentPackage = null; 3855 } 3856 } 3857 3858 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 3859 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 3860 mPackageInstallers.put(pkg, info.installerPackageName); 3861 // We've read only the manifest content itself at this point, 3862 // so consume the footer before looping around to the next 3863 // input file 3864 skipTarPadding(info.size, instream); 3865 sendOnRestorePackage(pkg); 3866 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 3867 // Metadata blobs! 3868 readMetadata(info, instream); 3869 } else { 3870 // Non-manifest, so it's actual file data. Is this a package 3871 // we're ignoring? 3872 boolean okay = true; 3873 RestorePolicy policy = mPackagePolicies.get(pkg); 3874 switch (policy) { 3875 case IGNORE: 3876 okay = false; 3877 break; 3878 3879 case ACCEPT_IF_APK: 3880 // If we're in accept-if-apk state, then the first file we 3881 // see MUST be the apk. 3882 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 3883 if (DEBUG) Slog.d(TAG, "APK file; installing"); 3884 // Try to install the app. 3885 String installerName = mPackageInstallers.get(pkg); 3886 okay = installApk(info, installerName, instream); 3887 // good to go; promote to ACCEPT 3888 mPackagePolicies.put(pkg, (okay) 3889 ? RestorePolicy.ACCEPT 3890 : RestorePolicy.IGNORE); 3891 // At this point we've consumed this file entry 3892 // ourselves, so just strip the tar footer and 3893 // go on to the next file in the input stream 3894 skipTarPadding(info.size, instream); 3895 return true; 3896 } else { 3897 // File data before (or without) the apk. We can't 3898 // handle it coherently in this case so ignore it. 3899 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 3900 okay = false; 3901 } 3902 break; 3903 3904 case ACCEPT: 3905 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 3906 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 3907 // we can take the data without the apk, so we 3908 // *want* to do so. skip the apk by declaring this 3909 // one file not-okay without changing the restore 3910 // policy for the package. 3911 okay = false; 3912 } 3913 break; 3914 3915 default: 3916 // Something has gone dreadfully wrong when determining 3917 // the restore policy from the manifest. Ignore the 3918 // rest of this package's data. 3919 Slog.e(TAG, "Invalid policy from manifest"); 3920 okay = false; 3921 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 3922 break; 3923 } 3924 3925 // If the policy is satisfied, go ahead and set up to pipe the 3926 // data to the agent. 3927 if (DEBUG && okay && mAgent != null) { 3928 Slog.i(TAG, "Reusing existing agent instance"); 3929 } 3930 if (okay && mAgent == null) { 3931 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 3932 3933 try { 3934 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 3935 3936 // If we haven't sent any data to this app yet, we probably 3937 // need to clear it first. Check that. 3938 if (!mClearedPackages.contains(pkg)) { 3939 // apps with their own backup agents are 3940 // responsible for coherently managing a full 3941 // restore. 3942 if (mTargetApp.backupAgentName == null) { 3943 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 3944 clearApplicationDataSynchronous(pkg); 3945 } else { 3946 if (DEBUG) Slog.d(TAG, "backup agent (" 3947 + mTargetApp.backupAgentName + ") => no clear"); 3948 } 3949 mClearedPackages.add(pkg); 3950 } else { 3951 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 3952 } 3953 3954 // All set; now set up the IPC and launch the agent 3955 setUpPipes(); 3956 mAgent = bindToAgentSynchronous(mTargetApp, 3957 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 3958 mAgentPackage = pkg; 3959 } catch (IOException e) { 3960 // fall through to error handling 3961 } catch (NameNotFoundException e) { 3962 // fall through to error handling 3963 } 3964 3965 if (mAgent == null) { 3966 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 3967 okay = false; 3968 tearDownPipes(); 3969 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 3970 } 3971 } 3972 3973 // Sanity check: make sure we never give data to the wrong app. This 3974 // should never happen but a little paranoia here won't go amiss. 3975 if (okay && !pkg.equals(mAgentPackage)) { 3976 Slog.e(TAG, "Restoring data for " + pkg 3977 + " but agent is for " + mAgentPackage); 3978 okay = false; 3979 } 3980 3981 // At this point we have an agent ready to handle the full 3982 // restore data as well as a pipe for sending data to 3983 // that agent. Tell the agent to start reading from the 3984 // pipe. 3985 if (okay) { 3986 boolean agentSuccess = true; 3987 long toCopy = info.size; 3988 final int token = generateToken(); 3989 try { 3990 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 3991 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 3992 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 3993 + " : " + info.path); 3994 mObbConnection.restoreObbFile(pkg, mPipes[0], 3995 info.size, info.type, info.path, info.mode, 3996 info.mtime, token, mBackupManagerBinder); 3997 } else { 3998 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 3999 + info.path); 4000 // fire up the app's agent listening on the socket. If 4001 // the agent is running in the system process we can't 4002 // just invoke it asynchronously, so we provide a thread 4003 // for it here. 4004 if (mTargetApp.processName.equals("system")) { 4005 Slog.d(TAG, "system process agent - spinning a thread"); 4006 RestoreFileRunnable runner = new RestoreFileRunnable( 4007 mAgent, info, mPipes[0], token); 4008 new Thread(runner, "restore-sys-runner").start(); 4009 } else { 4010 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 4011 info.domain, info.path, info.mode, info.mtime, 4012 token, mBackupManagerBinder); 4013 } 4014 } 4015 } catch (IOException e) { 4016 // couldn't dup the socket for a process-local restore 4017 Slog.d(TAG, "Couldn't establish restore"); 4018 agentSuccess = false; 4019 okay = false; 4020 } catch (RemoteException e) { 4021 // whoops, remote entity went away. We'll eat the content 4022 // ourselves, then, and not copy it over. 4023 Slog.e(TAG, "Agent crashed during full restore"); 4024 agentSuccess = false; 4025 okay = false; 4026 } 4027 4028 // Copy over the data if the agent is still good 4029 if (okay) { 4030 if (MORE_DEBUG) { 4031 Slog.v(TAG, " copying to restore agent: " 4032 + toCopy + " bytes"); 4033 } 4034 boolean pipeOkay = true; 4035 FileOutputStream pipe = new FileOutputStream( 4036 mPipes[1].getFileDescriptor()); 4037 while (toCopy > 0) { 4038 int toRead = (toCopy > mBuffer.length) 4039 ? mBuffer.length : (int)toCopy; 4040 int nRead = instream.read(mBuffer, 0, toRead); 4041 if (nRead >= 0) mBytes += nRead; 4042 if (nRead <= 0) break; 4043 toCopy -= nRead; 4044 4045 // send it to the output pipe as long as things 4046 // are still good 4047 if (pipeOkay) { 4048 try { 4049 pipe.write(mBuffer, 0, nRead); 4050 } catch (IOException e) { 4051 Slog.e(TAG, "Failed to write to restore pipe", e); 4052 pipeOkay = false; 4053 } 4054 } 4055 } 4056 4057 // done sending that file! Now we just need to consume 4058 // the delta from info.size to the end of block. 4059 skipTarPadding(info.size, instream); 4060 4061 // and now that we've sent it all, wait for the remote 4062 // side to acknowledge receipt 4063 agentSuccess = waitUntilOperationComplete(token); 4064 } 4065 4066 // okay, if the remote end failed at any point, deal with 4067 // it by ignoring the rest of the restore on it 4068 if (!agentSuccess) { 4069 mBackupHandler.removeMessages(MSG_TIMEOUT); 4070 tearDownPipes(); 4071 tearDownAgent(mTargetApp); 4072 mAgent = null; 4073 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 4074 4075 // If this was a single-package restore, we halt immediately 4076 // with an agent error under these circumstances 4077 if (mOnlyPackage != null) { 4078 setResult(RestoreEngine.TARGET_FAILURE); 4079 setRunning(false); 4080 return false; 4081 } 4082 } 4083 } 4084 4085 // Problems setting up the agent communication, or an already- 4086 // ignored package: skip to the next tar stream entry by 4087 // reading and discarding this file. 4088 if (!okay) { 4089 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 4090 long bytesToConsume = (info.size + 511) & ~511; 4091 while (bytesToConsume > 0) { 4092 int toRead = (bytesToConsume > mBuffer.length) 4093 ? mBuffer.length : (int)bytesToConsume; 4094 long nRead = instream.read(mBuffer, 0, toRead); 4095 if (nRead >= 0) mBytes += nRead; 4096 if (nRead <= 0) break; 4097 bytesToConsume -= nRead; 4098 } 4099 } 4100 } 4101 } 4102 } catch (IOException e) { 4103 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 4104 setResult(RestoreEngine.TRANSPORT_FAILURE); 4105 info = null; 4106 } 4107 4108 // If we got here we're either running smoothly or we've finished 4109 if (info == null) { 4110 setRunning(false); 4111 } 4112 return (info != null); 4113 } 4114 4115 void setUpPipes() throws IOException { 4116 mPipes = ParcelFileDescriptor.createPipe(); 4117 } 4118 4119 void tearDownPipes() { 4120 if (mPipes != null) { 4121 try { 4122 mPipes[0].close(); 4123 mPipes[0] = null; 4124 mPipes[1].close(); 4125 mPipes[1] = null; 4126 } catch (IOException e) { 4127 Slog.w(TAG, "Couldn't close agent pipes", e); 4128 } 4129 mPipes = null; 4130 } 4131 } 4132 4133 void tearDownAgent(ApplicationInfo app) { 4134 if (mAgent != null) { 4135 try { 4136 // unbind and tidy up even on timeout or failure, just in case 4137 mActivityManager.unbindBackupAgent(app); 4138 4139 // The agent was running with a stub Application object, so shut it down. 4140 // !!! We hardcode the confirmation UI's package name here rather than use a 4141 // manifest flag! TODO something less direct. 4142 if (app.uid != Process.SYSTEM_UID 4143 && !app.packageName.equals("com.android.backupconfirm")) { 4144 if (DEBUG) Slog.d(TAG, "Killing host process"); 4145 mActivityManager.killApplicationProcess(app.processName, app.uid); 4146 } else { 4147 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 4148 } 4149 } catch (RemoteException e) { 4150 Slog.d(TAG, "Lost app trying to shut down"); 4151 } 4152 mAgent = null; 4153 } 4154 } 4155 4156 class RestoreInstallObserver extends IPackageInstallObserver.Stub { 4157 final AtomicBoolean mDone = new AtomicBoolean(); 4158 String mPackageName; 4159 int mResult; 4160 4161 public void reset() { 4162 synchronized (mDone) { 4163 mDone.set(false); 4164 } 4165 } 4166 4167 public void waitForCompletion() { 4168 synchronized (mDone) { 4169 while (mDone.get() == false) { 4170 try { 4171 mDone.wait(); 4172 } catch (InterruptedException e) { } 4173 } 4174 } 4175 } 4176 4177 int getResult() { 4178 return mResult; 4179 } 4180 4181 @Override 4182 public void packageInstalled(String packageName, int returnCode) 4183 throws RemoteException { 4184 synchronized (mDone) { 4185 mResult = returnCode; 4186 mPackageName = packageName; 4187 mDone.set(true); 4188 mDone.notifyAll(); 4189 } 4190 } 4191 } 4192 4193 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 4194 final AtomicBoolean mDone = new AtomicBoolean(); 4195 int mResult; 4196 4197 public void reset() { 4198 synchronized (mDone) { 4199 mDone.set(false); 4200 } 4201 } 4202 4203 public void waitForCompletion() { 4204 synchronized (mDone) { 4205 while (mDone.get() == false) { 4206 try { 4207 mDone.wait(); 4208 } catch (InterruptedException e) { } 4209 } 4210 } 4211 } 4212 4213 @Override 4214 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 4215 synchronized (mDone) { 4216 mResult = returnCode; 4217 mDone.set(true); 4218 mDone.notifyAll(); 4219 } 4220 } 4221 } 4222 4223 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 4224 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 4225 4226 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 4227 boolean okay = true; 4228 4229 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 4230 4231 // The file content is an .apk file. Copy it out to a staging location and 4232 // attempt to install it. 4233 File apkFile = new File(mDataDir, info.packageName); 4234 try { 4235 FileOutputStream apkStream = new FileOutputStream(apkFile); 4236 byte[] buffer = new byte[32 * 1024]; 4237 long size = info.size; 4238 while (size > 0) { 4239 long toRead = (buffer.length < size) ? buffer.length : size; 4240 int didRead = instream.read(buffer, 0, (int)toRead); 4241 if (didRead >= 0) mBytes += didRead; 4242 apkStream.write(buffer, 0, didRead); 4243 size -= didRead; 4244 } 4245 apkStream.close(); 4246 4247 // make sure the installer can read it 4248 apkFile.setReadable(true, false); 4249 4250 // Now install it 4251 Uri packageUri = Uri.fromFile(apkFile); 4252 mInstallObserver.reset(); 4253 mPackageManager.installPackage(packageUri, mInstallObserver, 4254 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 4255 installerPackage); 4256 mInstallObserver.waitForCompletion(); 4257 4258 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 4259 // The only time we continue to accept install of data even if the 4260 // apk install failed is if we had already determined that we could 4261 // accept the data regardless. 4262 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 4263 okay = false; 4264 } 4265 } else { 4266 // Okay, the install succeeded. Make sure it was the right app. 4267 boolean uninstall = false; 4268 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 4269 Slog.w(TAG, "Restore stream claimed to include apk for " 4270 + info.packageName + " but apk was really " 4271 + mInstallObserver.mPackageName); 4272 // delete the package we just put in place; it might be fraudulent 4273 okay = false; 4274 uninstall = true; 4275 } else { 4276 try { 4277 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 4278 PackageManager.GET_SIGNATURES); 4279 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 4280 Slog.w(TAG, "Restore stream contains apk of package " 4281 + info.packageName + " but it disallows backup/restore"); 4282 okay = false; 4283 } else { 4284 // So far so good -- do the signatures match the manifest? 4285 Signature[] sigs = mManifestSignatures.get(info.packageName); 4286 if (signaturesMatch(sigs, pkg)) { 4287 // If this is a system-uid app without a declared backup agent, 4288 // don't restore any of the file data. 4289 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 4290 && (pkg.applicationInfo.backupAgentName == null)) { 4291 Slog.w(TAG, "Installed app " + info.packageName 4292 + " has restricted uid and no agent"); 4293 okay = false; 4294 } 4295 } else { 4296 Slog.w(TAG, "Installed app " + info.packageName 4297 + " signatures do not match restore manifest"); 4298 okay = false; 4299 uninstall = true; 4300 } 4301 } 4302 } catch (NameNotFoundException e) { 4303 Slog.w(TAG, "Install of package " + info.packageName 4304 + " succeeded but now not found"); 4305 okay = false; 4306 } 4307 } 4308 4309 // If we're not okay at this point, we need to delete the package 4310 // that we just installed. 4311 if (uninstall) { 4312 mDeleteObserver.reset(); 4313 mPackageManager.deletePackage(mInstallObserver.mPackageName, 4314 mDeleteObserver, 0); 4315 mDeleteObserver.waitForCompletion(); 4316 } 4317 } 4318 } catch (IOException e) { 4319 Slog.e(TAG, "Unable to transcribe restored apk for install"); 4320 okay = false; 4321 } finally { 4322 apkFile.delete(); 4323 } 4324 4325 return okay; 4326 } 4327 4328 // Given an actual file content size, consume the post-content padding mandated 4329 // by the tar format. 4330 void skipTarPadding(long size, InputStream instream) throws IOException { 4331 long partial = (size + 512) % 512; 4332 if (partial > 0) { 4333 final int needed = 512 - (int)partial; 4334 if (MORE_DEBUG) { 4335 Slog.i(TAG, "Skipping tar padding: " + needed + " bytes"); 4336 } 4337 byte[] buffer = new byte[needed]; 4338 if (readExactly(instream, buffer, 0, needed) == needed) { 4339 mBytes += needed; 4340 } else throw new IOException("Unexpected EOF in padding"); 4341 } 4342 } 4343 4344 // Read a widget metadata file, returning the restored blob 4345 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 4346 // Fail on suspiciously large widget dump files 4347 if (info.size > 64 * 1024) { 4348 throw new IOException("Metadata too big; corrupt? size=" + info.size); 4349 } 4350 4351 byte[] buffer = new byte[(int) info.size]; 4352 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 4353 mBytes += info.size; 4354 } else throw new IOException("Unexpected EOF in widget data"); 4355 4356 String[] str = new String[1]; 4357 int offset = extractLine(buffer, 0, str); 4358 int version = Integer.parseInt(str[0]); 4359 if (version == BACKUP_MANIFEST_VERSION) { 4360 offset = extractLine(buffer, offset, str); 4361 final String pkg = str[0]; 4362 if (info.packageName.equals(pkg)) { 4363 // Data checks out -- the rest of the buffer is a concatenation of 4364 // binary blobs as described in the comment at writeAppWidgetData() 4365 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 4366 offset, buffer.length - offset); 4367 DataInputStream in = new DataInputStream(bin); 4368 while (bin.available() > 0) { 4369 int token = in.readInt(); 4370 int size = in.readInt(); 4371 if (size > 64 * 1024) { 4372 throw new IOException("Datum " 4373 + Integer.toHexString(token) 4374 + " too big; corrupt? size=" + info.size); 4375 } 4376 switch (token) { 4377 case BACKUP_WIDGET_METADATA_TOKEN: 4378 { 4379 if (MORE_DEBUG) { 4380 Slog.i(TAG, "Got widget metadata for " + info.packageName); 4381 } 4382 mWidgetData = new byte[size]; 4383 in.read(mWidgetData); 4384 break; 4385 } 4386 default: 4387 { 4388 if (DEBUG) { 4389 Slog.i(TAG, "Ignoring metadata blob " 4390 + Integer.toHexString(token) 4391 + " for " + info.packageName); 4392 } 4393 in.skipBytes(size); 4394 break; 4395 } 4396 } 4397 } 4398 } else { 4399 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 4400 + " but widget data for " + pkg); 4401 } 4402 } else { 4403 Slog.w(TAG, "Unsupported metadata version " + version); 4404 } 4405 } 4406 4407 // Returns a policy constant 4408 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 4409 throws IOException { 4410 // Fail on suspiciously large manifest files 4411 if (info.size > 64 * 1024) { 4412 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 4413 } 4414 4415 byte[] buffer = new byte[(int) info.size]; 4416 if (MORE_DEBUG) { 4417 Slog.i(TAG, " readAppManifest() looking for " + info.size + " bytes, " 4418 + mBytes + " already consumed"); 4419 } 4420 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 4421 mBytes += info.size; 4422 } else throw new IOException("Unexpected EOF in manifest"); 4423 4424 RestorePolicy policy = RestorePolicy.IGNORE; 4425 String[] str = new String[1]; 4426 int offset = 0; 4427 4428 try { 4429 offset = extractLine(buffer, offset, str); 4430 int version = Integer.parseInt(str[0]); 4431 if (version == BACKUP_MANIFEST_VERSION) { 4432 offset = extractLine(buffer, offset, str); 4433 String manifestPackage = str[0]; 4434 // TODO: handle <original-package> 4435 if (manifestPackage.equals(info.packageName)) { 4436 offset = extractLine(buffer, offset, str); 4437 version = Integer.parseInt(str[0]); // app version 4438 offset = extractLine(buffer, offset, str); 4439 int platformVersion = Integer.parseInt(str[0]); 4440 offset = extractLine(buffer, offset, str); 4441 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 4442 offset = extractLine(buffer, offset, str); 4443 boolean hasApk = str[0].equals("1"); 4444 offset = extractLine(buffer, offset, str); 4445 int numSigs = Integer.parseInt(str[0]); 4446 if (numSigs > 0) { 4447 Signature[] sigs = new Signature[numSigs]; 4448 for (int i = 0; i < numSigs; i++) { 4449 offset = extractLine(buffer, offset, str); 4450 sigs[i] = new Signature(str[0]); 4451 } 4452 mManifestSignatures.put(info.packageName, sigs); 4453 4454 // Okay, got the manifest info we need... 4455 try { 4456 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 4457 info.packageName, PackageManager.GET_SIGNATURES); 4458 // Fall through to IGNORE if the app explicitly disallows backup 4459 final int flags = pkgInfo.applicationInfo.flags; 4460 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 4461 // Restore system-uid-space packages only if they have 4462 // defined a custom backup agent 4463 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 4464 || (pkgInfo.applicationInfo.backupAgentName != null)) { 4465 // Verify signatures against any installed version; if they 4466 // don't match, then we fall though and ignore the data. The 4467 // signatureMatch() method explicitly ignores the signature 4468 // check for packages installed on the system partition, because 4469 // such packages are signed with the platform cert instead of 4470 // the app developer's cert, so they're different on every 4471 // device. 4472 if (signaturesMatch(sigs, pkgInfo)) { 4473 if (pkgInfo.versionCode >= version) { 4474 Slog.i(TAG, "Sig + version match; taking data"); 4475 policy = RestorePolicy.ACCEPT; 4476 } else { 4477 // The data is from a newer version of the app than 4478 // is presently installed. That means we can only 4479 // use it if the matching apk is also supplied. 4480 if (mAllowApks) { 4481 Slog.i(TAG, "Data version " + version 4482 + " is newer than installed version " 4483 + pkgInfo.versionCode 4484 + " - requiring apk"); 4485 policy = RestorePolicy.ACCEPT_IF_APK; 4486 } else { 4487 Slog.i(TAG, "Data requires newer version " 4488 + version + "; ignoring"); 4489 policy = RestorePolicy.IGNORE; 4490 } 4491 } 4492 } else { 4493 Slog.w(TAG, "Restore manifest signatures do not match " 4494 + "installed application for " + info.packageName); 4495 } 4496 } else { 4497 Slog.w(TAG, "Package " + info.packageName 4498 + " is system level with no agent"); 4499 } 4500 } else { 4501 if (DEBUG) Slog.i(TAG, "Restore manifest from " 4502 + info.packageName + " but allowBackup=false"); 4503 } 4504 } catch (NameNotFoundException e) { 4505 // Okay, the target app isn't installed. We can process 4506 // the restore properly only if the dataset provides the 4507 // apk file and we can successfully install it. 4508 if (mAllowApks) { 4509 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 4510 + " not installed; requiring apk in dataset"); 4511 policy = RestorePolicy.ACCEPT_IF_APK; 4512 } else { 4513 policy = RestorePolicy.IGNORE; 4514 } 4515 } 4516 4517 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 4518 Slog.i(TAG, "Cannot restore package " + info.packageName 4519 + " without the matching .apk"); 4520 } 4521 } else { 4522 Slog.i(TAG, "Missing signature on backed-up package " 4523 + info.packageName); 4524 } 4525 } else { 4526 Slog.i(TAG, "Expected package " + info.packageName 4527 + " but restore manifest claims " + manifestPackage); 4528 } 4529 } else { 4530 Slog.i(TAG, "Unknown restore manifest version " + version 4531 + " for package " + info.packageName); 4532 } 4533 } catch (NumberFormatException e) { 4534 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 4535 } catch (IllegalArgumentException e) { 4536 Slog.w(TAG, e.getMessage()); 4537 } 4538 4539 return policy; 4540 } 4541 4542 // Builds a line from a byte buffer starting at 'offset', and returns 4543 // the index of the next unconsumed data in the buffer. 4544 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 4545 final int end = buffer.length; 4546 if (offset >= end) throw new IOException("Incomplete data"); 4547 4548 int pos; 4549 for (pos = offset; pos < end; pos++) { 4550 byte c = buffer[pos]; 4551 // at LF we declare end of line, and return the next char as the 4552 // starting point for the next time through 4553 if (c == '\n') { 4554 break; 4555 } 4556 } 4557 outStr[0] = new String(buffer, offset, pos - offset); 4558 pos++; // may be pointing an extra byte past the end but that's okay 4559 return pos; 4560 } 4561 4562 void dumpFileMetadata(FileMetadata info) { 4563 if (DEBUG) { 4564 StringBuilder b = new StringBuilder(128); 4565 4566 // mode string 4567 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 4568 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 4569 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 4570 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 4571 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 4572 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 4573 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 4574 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 4575 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 4576 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 4577 b.append(String.format(" %9d ", info.size)); 4578 4579 Date stamp = new Date(info.mtime); 4580 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 4581 4582 b.append(info.packageName); 4583 b.append(" :: "); 4584 b.append(info.domain); 4585 b.append(" :: "); 4586 b.append(info.path); 4587 4588 Slog.i(TAG, b.toString()); 4589 } 4590 } 4591 4592 // Consume a tar file header block [sequence] and accumulate the relevant metadata 4593 FileMetadata readTarHeaders(InputStream instream) throws IOException { 4594 byte[] block = new byte[512]; 4595 FileMetadata info = null; 4596 4597 boolean gotHeader = readTarHeader(instream, block); 4598 if (gotHeader) { 4599 try { 4600 // okay, presume we're okay, and extract the various metadata 4601 info = new FileMetadata(); 4602 info.size = extractRadix(block, 124, 12, 8); 4603 info.mtime = extractRadix(block, 136, 12, 8); 4604 info.mode = extractRadix(block, 100, 8, 8); 4605 4606 info.path = extractString(block, 345, 155); // prefix 4607 String path = extractString(block, 0, 100); 4608 if (path.length() > 0) { 4609 if (info.path.length() > 0) info.path += '/'; 4610 info.path += path; 4611 } 4612 4613 // tar link indicator field: 1 byte at offset 156 in the header. 4614 int typeChar = block[156]; 4615 if (typeChar == 'x') { 4616 // pax extended header, so we need to read that 4617 gotHeader = readPaxExtendedHeader(instream, info); 4618 if (gotHeader) { 4619 // and after a pax extended header comes another real header -- read 4620 // that to find the real file type 4621 gotHeader = readTarHeader(instream, block); 4622 } 4623 if (!gotHeader) throw new IOException("Bad or missing pax header"); 4624 4625 typeChar = block[156]; 4626 } 4627 4628 switch (typeChar) { 4629 case '0': info.type = BackupAgent.TYPE_FILE; break; 4630 case '5': { 4631 info.type = BackupAgent.TYPE_DIRECTORY; 4632 if (info.size != 0) { 4633 Slog.w(TAG, "Directory entry with nonzero size in header"); 4634 info.size = 0; 4635 } 4636 break; 4637 } 4638 case 0: { 4639 // presume EOF 4640 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 4641 return null; 4642 } 4643 default: { 4644 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 4645 throw new IOException("Unknown entity type " + typeChar); 4646 } 4647 } 4648 4649 // Parse out the path 4650 // 4651 // first: apps/shared/unrecognized 4652 if (FullBackup.SHARED_PREFIX.regionMatches(0, 4653 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 4654 // File in shared storage. !!! TODO: implement this. 4655 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 4656 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 4657 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 4658 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 4659 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 4660 info.path, 0, FullBackup.APPS_PREFIX.length())) { 4661 // App content! Parse out the package name and domain 4662 4663 // strip the apps/ prefix 4664 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 4665 4666 // extract the package name 4667 int slash = info.path.indexOf('/'); 4668 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 4669 info.packageName = info.path.substring(0, slash); 4670 info.path = info.path.substring(slash+1); 4671 4672 // if it's a manifest we're done, otherwise parse out the domains 4673 if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) { 4674 slash = info.path.indexOf('/'); 4675 if (slash < 0) { 4676 throw new IOException("Illegal semantic path in non-manifest " 4677 + info.path); 4678 } 4679 info.domain = info.path.substring(0, slash); 4680 info.path = info.path.substring(slash + 1); 4681 } 4682 } 4683 } catch (IOException e) { 4684 if (DEBUG) { 4685 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 4686 HEXLOG(block); 4687 } 4688 throw e; 4689 } 4690 } 4691 return info; 4692 } 4693 4694 private void HEXLOG(byte[] block) { 4695 int offset = 0; 4696 int todo = block.length; 4697 StringBuilder buf = new StringBuilder(64); 4698 while (todo > 0) { 4699 buf.append(String.format("%04x ", offset)); 4700 int numThisLine = (todo > 16) ? 16 : todo; 4701 for (int i = 0; i < numThisLine; i++) { 4702 buf.append(String.format("%02x ", block[offset+i])); 4703 } 4704 Slog.i("hexdump", buf.toString()); 4705 buf.setLength(0); 4706 todo -= numThisLine; 4707 offset += numThisLine; 4708 } 4709 } 4710 4711 // Read exactly the given number of bytes into a buffer at the stated offset. 4712 // Returns false if EOF is encountered before the requested number of bytes 4713 // could be read. 4714 int readExactly(InputStream in, byte[] buffer, int offset, int size) 4715 throws IOException { 4716 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 4717if (MORE_DEBUG) Slog.i(TAG, " ... readExactly(" + size + ") called"); 4718 int soFar = 0; 4719 while (soFar < size) { 4720 int nRead = in.read(buffer, offset + soFar, size - soFar); 4721 if (nRead <= 0) { 4722 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 4723 break; 4724 } 4725 soFar += nRead; 4726if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar)); 4727 } 4728 return soFar; 4729 } 4730 4731 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 4732 final int got = readExactly(instream, block, 0, 512); 4733 if (got == 0) return false; // Clean EOF 4734 if (got < 512) throw new IOException("Unable to read full block header"); 4735 mBytes += 512; 4736 return true; 4737 } 4738 4739 // overwrites 'info' fields based on the pax extended header 4740 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 4741 throws IOException { 4742 // We should never see a pax extended header larger than this 4743 if (info.size > 32*1024) { 4744 Slog.w(TAG, "Suspiciously large pax header size " + info.size 4745 + " - aborting"); 4746 throw new IOException("Sanity failure: pax header size " + info.size); 4747 } 4748 4749 // read whole blocks, not just the content size 4750 int numBlocks = (int)((info.size + 511) >> 9); 4751 byte[] data = new byte[numBlocks * 512]; 4752 if (readExactly(instream, data, 0, data.length) < data.length) { 4753 throw new IOException("Unable to read full pax header"); 4754 } 4755 mBytes += data.length; 4756 4757 final int contentSize = (int) info.size; 4758 int offset = 0; 4759 do { 4760 // extract the line at 'offset' 4761 int eol = offset+1; 4762 while (eol < contentSize && data[eol] != ' ') eol++; 4763 if (eol >= contentSize) { 4764 // error: we just hit EOD looking for the end of the size field 4765 throw new IOException("Invalid pax data"); 4766 } 4767 // eol points to the space between the count and the key 4768 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 4769 int key = eol + 1; // start of key=value 4770 eol = offset + linelen - 1; // trailing LF 4771 int value; 4772 for (value = key+1; data[value] != '=' && value <= eol; value++); 4773 if (value > eol) { 4774 throw new IOException("Invalid pax declaration"); 4775 } 4776 4777 // pax requires that key/value strings be in UTF-8 4778 String keyStr = new String(data, key, value-key, "UTF-8"); 4779 // -1 to strip the trailing LF 4780 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 4781 4782 if ("path".equals(keyStr)) { 4783 info.path = valStr; 4784 } else if ("size".equals(keyStr)) { 4785 info.size = Long.parseLong(valStr); 4786 } else { 4787 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 4788 } 4789 4790 offset += linelen; 4791 } while (offset < contentSize); 4792 4793 return true; 4794 } 4795 4796 long extractRadix(byte[] data, int offset, int maxChars, int radix) 4797 throws IOException { 4798 long value = 0; 4799 final int end = offset + maxChars; 4800 for (int i = offset; i < end; i++) { 4801 final byte b = data[i]; 4802 // Numeric fields in tar can terminate with either NUL or SPC 4803 if (b == 0 || b == ' ') break; 4804 if (b < '0' || b > ('0' + radix - 1)) { 4805 throw new IOException("Invalid number in header: '" + (char)b 4806 + "' for radix " + radix); 4807 } 4808 value = radix * value + (b - '0'); 4809 } 4810 return value; 4811 } 4812 4813 String extractString(byte[] data, int offset, int maxChars) throws IOException { 4814 final int end = offset + maxChars; 4815 int eos = offset; 4816 // tar string fields terminate early with a NUL 4817 while (eos < end && data[eos] != 0) eos++; 4818 return new String(data, offset, eos-offset, "US-ASCII"); 4819 } 4820 4821 void sendStartRestore() { 4822 if (mObserver != null) { 4823 try { 4824 mObserver.onStartRestore(); 4825 } catch (RemoteException e) { 4826 Slog.w(TAG, "full restore observer went away: startRestore"); 4827 mObserver = null; 4828 } 4829 } 4830 } 4831 4832 void sendOnRestorePackage(String name) { 4833 if (mObserver != null) { 4834 try { 4835 // TODO: use a more user-friendly name string 4836 mObserver.onRestorePackage(name); 4837 } catch (RemoteException e) { 4838 Slog.w(TAG, "full restore observer went away: restorePackage"); 4839 mObserver = null; 4840 } 4841 } 4842 } 4843 4844 void sendEndRestore() { 4845 if (mObserver != null) { 4846 try { 4847 mObserver.onEndRestore(); 4848 } catch (RemoteException e) { 4849 Slog.w(TAG, "full restore observer went away: endRestore"); 4850 mObserver = null; 4851 } 4852 } 4853 } 4854 } 4855 4856 // ***** end new engine class *** 4857 4858 class PerformAdbRestoreTask implements Runnable { 4859 ParcelFileDescriptor mInputFile; 4860 String mCurrentPassword; 4861 String mDecryptPassword; 4862 IFullBackupRestoreObserver mObserver; 4863 AtomicBoolean mLatchObject; 4864 IBackupAgent mAgent; 4865 String mAgentPackage; 4866 ApplicationInfo mTargetApp; 4867 FullBackupObbConnection mObbConnection = null; 4868 ParcelFileDescriptor[] mPipes = null; 4869 byte[] mWidgetData = null; 4870 4871 long mBytes; 4872 4873 // possible handling states for a given package in the restore dataset 4874 final HashMap<String, RestorePolicy> mPackagePolicies 4875 = new HashMap<String, RestorePolicy>(); 4876 4877 // installer package names for each encountered app, derived from the manifests 4878 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 4879 4880 // Signatures for a given package found in its manifest file 4881 final HashMap<String, Signature[]> mManifestSignatures 4882 = new HashMap<String, Signature[]>(); 4883 4884 // Packages we've already wiped data on when restoring their first file 4885 final HashSet<String> mClearedPackages = new HashSet<String>(); 4886 4887 PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword, 4888 IFullBackupRestoreObserver observer, AtomicBoolean latch) { 4889 mInputFile = fd; 4890 mCurrentPassword = curPassword; 4891 mDecryptPassword = decryptPassword; 4892 mObserver = observer; 4893 mLatchObject = latch; 4894 mAgent = null; 4895 mAgentPackage = null; 4896 mTargetApp = null; 4897 mObbConnection = new FullBackupObbConnection(); 4898 4899 // Which packages we've already wiped data on. We prepopulate this 4900 // with a whitelist of packages known to be unclearable. 4901 mClearedPackages.add("android"); 4902 mClearedPackages.add(SETTINGS_PACKAGE); 4903 } 4904 4905 class RestoreFileRunnable implements Runnable { 4906 IBackupAgent mAgent; 4907 FileMetadata mInfo; 4908 ParcelFileDescriptor mSocket; 4909 int mToken; 4910 4911 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 4912 ParcelFileDescriptor socket, int token) throws IOException { 4913 mAgent = agent; 4914 mInfo = info; 4915 mToken = token; 4916 4917 // This class is used strictly for process-local binder invocations. The 4918 // semantics of ParcelFileDescriptor differ in this case; in particular, we 4919 // do not automatically get a 'dup'ed descriptor that we can can continue 4920 // to use asynchronously from the caller. So, we make sure to dup it ourselves 4921 // before proceeding to do the restore. 4922 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 4923 } 4924 4925 @Override 4926 public void run() { 4927 try { 4928 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 4929 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 4930 mToken, mBackupManagerBinder); 4931 } catch (RemoteException e) { 4932 // never happens; this is used strictly for local binder calls 4933 } 4934 } 4935 } 4936 4937 @Override 4938 public void run() { 4939 Slog.i(TAG, "--- Performing full-dataset restore ---"); 4940 mObbConnection.establish(); 4941 sendStartRestore(); 4942 4943 // Are we able to restore shared-storage data? 4944 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 4945 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT); 4946 } 4947 4948 FileInputStream rawInStream = null; 4949 DataInputStream rawDataIn = null; 4950 try { 4951 if (!backupPasswordMatches(mCurrentPassword)) { 4952 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 4953 return; 4954 } 4955 4956 mBytes = 0; 4957 byte[] buffer = new byte[32 * 1024]; 4958 rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); 4959 rawDataIn = new DataInputStream(rawInStream); 4960 4961 // First, parse out the unencrypted/uncompressed header 4962 boolean compressed = false; 4963 InputStream preCompressStream = rawInStream; 4964 final InputStream in; 4965 4966 boolean okay = false; 4967 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length(); 4968 byte[] streamHeader = new byte[headerLen]; 4969 rawDataIn.readFully(streamHeader); 4970 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8"); 4971 if (Arrays.equals(magicBytes, streamHeader)) { 4972 // okay, header looks good. now parse out the rest of the fields. 4973 String s = readHeaderLine(rawInStream); 4974 final int archiveVersion = Integer.parseInt(s); 4975 if (archiveVersion <= BACKUP_FILE_VERSION) { 4976 // okay, it's a version we recognize. if it's version 1, we may need 4977 // to try two different PBKDF2 regimes to compare checksums. 4978 final boolean pbkdf2Fallback = (archiveVersion == 1); 4979 4980 s = readHeaderLine(rawInStream); 4981 compressed = (Integer.parseInt(s) != 0); 4982 s = readHeaderLine(rawInStream); 4983 if (s.equals("none")) { 4984 // no more header to parse; we're good to go 4985 okay = true; 4986 } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) { 4987 preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback, 4988 rawInStream); 4989 if (preCompressStream != null) { 4990 okay = true; 4991 } 4992 } else Slog.w(TAG, "Archive is encrypted but no password given"); 4993 } else Slog.w(TAG, "Wrong header version: " + s); 4994 } else Slog.w(TAG, "Didn't read the right header magic"); 4995 4996 if (!okay) { 4997 Slog.w(TAG, "Invalid restore data; aborting."); 4998 return; 4999 } 5000 5001 // okay, use the right stream layer based on compression 5002 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream; 5003 5004 boolean didRestore; 5005 do { 5006 didRestore = restoreOneFile(in, buffer); 5007 } while (didRestore); 5008 5009 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); 5010 } catch (IOException e) { 5011 Slog.e(TAG, "Unable to read restore input"); 5012 } finally { 5013 tearDownPipes(); 5014 tearDownAgent(mTargetApp); 5015 5016 try { 5017 if (rawDataIn != null) rawDataIn.close(); 5018 if (rawInStream != null) rawInStream.close(); 5019 mInputFile.close(); 5020 } catch (IOException e) { 5021 Slog.w(TAG, "Close of restore data pipe threw", e); 5022 /* nothing we can do about this */ 5023 } 5024 synchronized (mCurrentOpLock) { 5025 mCurrentOperations.clear(); 5026 } 5027 synchronized (mLatchObject) { 5028 mLatchObject.set(true); 5029 mLatchObject.notifyAll(); 5030 } 5031 mObbConnection.tearDown(); 5032 sendEndRestore(); 5033 Slog.d(TAG, "Full restore pass complete."); 5034 mWakelock.release(); 5035 } 5036 } 5037 5038 String readHeaderLine(InputStream in) throws IOException { 5039 int c; 5040 StringBuilder buffer = new StringBuilder(80); 5041 while ((c = in.read()) >= 0) { 5042 if (c == '\n') break; // consume and discard the newlines 5043 buffer.append((char)c); 5044 } 5045 return buffer.toString(); 5046 } 5047 5048 InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt, 5049 int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream, 5050 boolean doLog) { 5051 InputStream result = null; 5052 5053 try { 5054 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 5055 SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt, 5056 rounds); 5057 byte[] IV = hexToByteArray(userIvHex); 5058 IvParameterSpec ivSpec = new IvParameterSpec(IV); 5059 c.init(Cipher.DECRYPT_MODE, 5060 new SecretKeySpec(userKey.getEncoded(), "AES"), 5061 ivSpec); 5062 byte[] mkCipher = hexToByteArray(masterKeyBlobHex); 5063 byte[] mkBlob = c.doFinal(mkCipher); 5064 5065 // first, the master key IV 5066 int offset = 0; 5067 int len = mkBlob[offset++]; 5068 IV = Arrays.copyOfRange(mkBlob, offset, offset + len); 5069 offset += len; 5070 // then the master key itself 5071 len = mkBlob[offset++]; 5072 byte[] mk = Arrays.copyOfRange(mkBlob, 5073 offset, offset + len); 5074 offset += len; 5075 // and finally the master key checksum hash 5076 len = mkBlob[offset++]; 5077 byte[] mkChecksum = Arrays.copyOfRange(mkBlob, 5078 offset, offset + len); 5079 5080 // now validate the decrypted master key against the checksum 5081 byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds); 5082 if (Arrays.equals(calculatedCk, mkChecksum)) { 5083 ivSpec = new IvParameterSpec(IV); 5084 c.init(Cipher.DECRYPT_MODE, 5085 new SecretKeySpec(mk, "AES"), 5086 ivSpec); 5087 // Only if all of the above worked properly will 'result' be assigned 5088 result = new CipherInputStream(rawInStream, c); 5089 } else if (doLog) Slog.w(TAG, "Incorrect password"); 5090 } catch (InvalidAlgorithmParameterException e) { 5091 if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e); 5092 } catch (BadPaddingException e) { 5093 // This case frequently occurs when the wrong password is used to decrypt 5094 // the master key. Use the identical "incorrect password" log text as is 5095 // used in the checksum failure log in order to avoid providing additional 5096 // information to an attacker. 5097 if (doLog) Slog.w(TAG, "Incorrect password"); 5098 } catch (IllegalBlockSizeException e) { 5099 if (doLog) Slog.w(TAG, "Invalid block size in master key"); 5100 } catch (NoSuchAlgorithmException e) { 5101 if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!"); 5102 } catch (NoSuchPaddingException e) { 5103 if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!"); 5104 } catch (InvalidKeyException e) { 5105 if (doLog) Slog.w(TAG, "Illegal password; aborting"); 5106 } 5107 5108 return result; 5109 } 5110 5111 InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback, 5112 InputStream rawInStream) { 5113 InputStream result = null; 5114 try { 5115 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) { 5116 5117 String userSaltHex = readHeaderLine(rawInStream); // 5 5118 byte[] userSalt = hexToByteArray(userSaltHex); 5119 5120 String ckSaltHex = readHeaderLine(rawInStream); // 6 5121 byte[] ckSalt = hexToByteArray(ckSaltHex); 5122 5123 int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 5124 String userIvHex = readHeaderLine(rawInStream); // 8 5125 5126 String masterKeyBlobHex = readHeaderLine(rawInStream); // 9 5127 5128 // decrypt the master key blob 5129 result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt, 5130 rounds, userIvHex, masterKeyBlobHex, rawInStream, false); 5131 if (result == null && pbkdf2Fallback) { 5132 result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt, 5133 rounds, userIvHex, masterKeyBlobHex, rawInStream, true); 5134 } 5135 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName); 5136 } catch (NumberFormatException e) { 5137 Slog.w(TAG, "Can't parse restore data header"); 5138 } catch (IOException e) { 5139 Slog.w(TAG, "Can't read input header"); 5140 } 5141 5142 return result; 5143 } 5144 5145 boolean restoreOneFile(InputStream instream, byte[] buffer) { 5146 FileMetadata info; 5147 try { 5148 info = readTarHeaders(instream); 5149 if (info != null) { 5150 if (MORE_DEBUG) { 5151 dumpFileMetadata(info); 5152 } 5153 5154 final String pkg = info.packageName; 5155 if (!pkg.equals(mAgentPackage)) { 5156 // okay, change in package; set up our various 5157 // bookkeeping if we haven't seen it yet 5158 if (!mPackagePolicies.containsKey(pkg)) { 5159 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5160 } 5161 5162 // Clean up the previous agent relationship if necessary, 5163 // and let the observer know we're considering a new app. 5164 if (mAgent != null) { 5165 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 5166 // Now we're really done 5167 tearDownPipes(); 5168 tearDownAgent(mTargetApp); 5169 mTargetApp = null; 5170 mAgentPackage = null; 5171 } 5172 } 5173 5174 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 5175 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 5176 mPackageInstallers.put(pkg, info.installerPackageName); 5177 // We've read only the manifest content itself at this point, 5178 // so consume the footer before looping around to the next 5179 // input file 5180 skipTarPadding(info.size, instream); 5181 sendOnRestorePackage(pkg); 5182 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 5183 // Metadata blobs! 5184 readMetadata(info, instream); 5185 } else { 5186 // Non-manifest, so it's actual file data. Is this a package 5187 // we're ignoring? 5188 boolean okay = true; 5189 RestorePolicy policy = mPackagePolicies.get(pkg); 5190 switch (policy) { 5191 case IGNORE: 5192 okay = false; 5193 break; 5194 5195 case ACCEPT_IF_APK: 5196 // If we're in accept-if-apk state, then the first file we 5197 // see MUST be the apk. 5198 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5199 if (DEBUG) Slog.d(TAG, "APK file; installing"); 5200 // Try to install the app. 5201 String installerName = mPackageInstallers.get(pkg); 5202 okay = installApk(info, installerName, instream); 5203 // good to go; promote to ACCEPT 5204 mPackagePolicies.put(pkg, (okay) 5205 ? RestorePolicy.ACCEPT 5206 : RestorePolicy.IGNORE); 5207 // At this point we've consumed this file entry 5208 // ourselves, so just strip the tar footer and 5209 // go on to the next file in the input stream 5210 skipTarPadding(info.size, instream); 5211 return true; 5212 } else { 5213 // File data before (or without) the apk. We can't 5214 // handle it coherently in this case so ignore it. 5215 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5216 okay = false; 5217 } 5218 break; 5219 5220 case ACCEPT: 5221 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5222 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 5223 // we can take the data without the apk, so we 5224 // *want* to do so. skip the apk by declaring this 5225 // one file not-okay without changing the restore 5226 // policy for the package. 5227 okay = false; 5228 } 5229 break; 5230 5231 default: 5232 // Something has gone dreadfully wrong when determining 5233 // the restore policy from the manifest. Ignore the 5234 // rest of this package's data. 5235 Slog.e(TAG, "Invalid policy from manifest"); 5236 okay = false; 5237 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5238 break; 5239 } 5240 5241 // If the policy is satisfied, go ahead and set up to pipe the 5242 // data to the agent. 5243 if (DEBUG && okay && mAgent != null) { 5244 Slog.i(TAG, "Reusing existing agent instance"); 5245 } 5246 if (okay && mAgent == null) { 5247 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 5248 5249 try { 5250 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 5251 5252 // If we haven't sent any data to this app yet, we probably 5253 // need to clear it first. Check that. 5254 if (!mClearedPackages.contains(pkg)) { 5255 // apps with their own backup agents are 5256 // responsible for coherently managing a full 5257 // restore. 5258 if (mTargetApp.backupAgentName == null) { 5259 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 5260 clearApplicationDataSynchronous(pkg); 5261 } else { 5262 if (DEBUG) Slog.d(TAG, "backup agent (" 5263 + mTargetApp.backupAgentName + ") => no clear"); 5264 } 5265 mClearedPackages.add(pkg); 5266 } else { 5267 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 5268 } 5269 5270 // All set; now set up the IPC and launch the agent 5271 setUpPipes(); 5272 mAgent = bindToAgentSynchronous(mTargetApp, 5273 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 5274 mAgentPackage = pkg; 5275 } catch (IOException e) { 5276 // fall through to error handling 5277 } catch (NameNotFoundException e) { 5278 // fall through to error handling 5279 } 5280 5281 if (mAgent == null) { 5282 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 5283 okay = false; 5284 tearDownPipes(); 5285 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5286 } 5287 } 5288 5289 // Sanity check: make sure we never give data to the wrong app. This 5290 // should never happen but a little paranoia here won't go amiss. 5291 if (okay && !pkg.equals(mAgentPackage)) { 5292 Slog.e(TAG, "Restoring data for " + pkg 5293 + " but agent is for " + mAgentPackage); 5294 okay = false; 5295 } 5296 5297 // At this point we have an agent ready to handle the full 5298 // restore data as well as a pipe for sending data to 5299 // that agent. Tell the agent to start reading from the 5300 // pipe. 5301 if (okay) { 5302 boolean agentSuccess = true; 5303 long toCopy = info.size; 5304 final int token = generateToken(); 5305 try { 5306 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 5307 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 5308 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 5309 + " : " + info.path); 5310 mObbConnection.restoreObbFile(pkg, mPipes[0], 5311 info.size, info.type, info.path, info.mode, 5312 info.mtime, token, mBackupManagerBinder); 5313 } else { 5314 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 5315 + info.path); 5316 // fire up the app's agent listening on the socket. If 5317 // the agent is running in the system process we can't 5318 // just invoke it asynchronously, so we provide a thread 5319 // for it here. 5320 if (mTargetApp.processName.equals("system")) { 5321 Slog.d(TAG, "system process agent - spinning a thread"); 5322 RestoreFileRunnable runner = new RestoreFileRunnable( 5323 mAgent, info, mPipes[0], token); 5324 new Thread(runner, "restore-sys-runner").start(); 5325 } else { 5326 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 5327 info.domain, info.path, info.mode, info.mtime, 5328 token, mBackupManagerBinder); 5329 } 5330 } 5331 } catch (IOException e) { 5332 // couldn't dup the socket for a process-local restore 5333 Slog.d(TAG, "Couldn't establish restore"); 5334 agentSuccess = false; 5335 okay = false; 5336 } catch (RemoteException e) { 5337 // whoops, remote entity went away. We'll eat the content 5338 // ourselves, then, and not copy it over. 5339 Slog.e(TAG, "Agent crashed during full restore"); 5340 agentSuccess = false; 5341 okay = false; 5342 } 5343 5344 // Copy over the data if the agent is still good 5345 if (okay) { 5346 boolean pipeOkay = true; 5347 FileOutputStream pipe = new FileOutputStream( 5348 mPipes[1].getFileDescriptor()); 5349 while (toCopy > 0) { 5350 int toRead = (toCopy > buffer.length) 5351 ? buffer.length : (int)toCopy; 5352 int nRead = instream.read(buffer, 0, toRead); 5353 if (nRead >= 0) mBytes += nRead; 5354 if (nRead <= 0) break; 5355 toCopy -= nRead; 5356 5357 // send it to the output pipe as long as things 5358 // are still good 5359 if (pipeOkay) { 5360 try { 5361 pipe.write(buffer, 0, nRead); 5362 } catch (IOException e) { 5363 Slog.e(TAG, "Failed to write to restore pipe", e); 5364 pipeOkay = false; 5365 } 5366 } 5367 } 5368 5369 // done sending that file! Now we just need to consume 5370 // the delta from info.size to the end of block. 5371 skipTarPadding(info.size, instream); 5372 5373 // and now that we've sent it all, wait for the remote 5374 // side to acknowledge receipt 5375 agentSuccess = waitUntilOperationComplete(token); 5376 } 5377 5378 // okay, if the remote end failed at any point, deal with 5379 // it by ignoring the rest of the restore on it 5380 if (!agentSuccess) { 5381 mBackupHandler.removeMessages(MSG_TIMEOUT); 5382 tearDownPipes(); 5383 tearDownAgent(mTargetApp); 5384 mAgent = null; 5385 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5386 } 5387 } 5388 5389 // Problems setting up the agent communication, or an already- 5390 // ignored package: skip to the next tar stream entry by 5391 // reading and discarding this file. 5392 if (!okay) { 5393 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 5394 long bytesToConsume = (info.size + 511) & ~511; 5395 while (bytesToConsume > 0) { 5396 int toRead = (bytesToConsume > buffer.length) 5397 ? buffer.length : (int)bytesToConsume; 5398 long nRead = instream.read(buffer, 0, toRead); 5399 if (nRead >= 0) mBytes += nRead; 5400 if (nRead <= 0) break; 5401 bytesToConsume -= nRead; 5402 } 5403 } 5404 } 5405 } 5406 } catch (IOException e) { 5407 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 5408 // treat as EOF 5409 info = null; 5410 } 5411 5412 return (info != null); 5413 } 5414 5415 void setUpPipes() throws IOException { 5416 mPipes = ParcelFileDescriptor.createPipe(); 5417 } 5418 5419 void tearDownPipes() { 5420 if (mPipes != null) { 5421 try { 5422 mPipes[0].close(); 5423 mPipes[0] = null; 5424 mPipes[1].close(); 5425 mPipes[1] = null; 5426 } catch (IOException e) { 5427 Slog.w(TAG, "Couldn't close agent pipes", e); 5428 } 5429 mPipes = null; 5430 } 5431 } 5432 5433 void tearDownAgent(ApplicationInfo app) { 5434 if (mAgent != null) { 5435 try { 5436 // unbind and tidy up even on timeout or failure, just in case 5437 mActivityManager.unbindBackupAgent(app); 5438 5439 // The agent was running with a stub Application object, so shut it down. 5440 // !!! We hardcode the confirmation UI's package name here rather than use a 5441 // manifest flag! TODO something less direct. 5442 if (app.uid != Process.SYSTEM_UID 5443 && !app.packageName.equals("com.android.backupconfirm")) { 5444 if (DEBUG) Slog.d(TAG, "Killing host process"); 5445 mActivityManager.killApplicationProcess(app.processName, app.uid); 5446 } else { 5447 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 5448 } 5449 } catch (RemoteException e) { 5450 Slog.d(TAG, "Lost app trying to shut down"); 5451 } 5452 mAgent = null; 5453 } 5454 } 5455 5456 class RestoreInstallObserver extends IPackageInstallObserver.Stub { 5457 final AtomicBoolean mDone = new AtomicBoolean(); 5458 String mPackageName; 5459 int mResult; 5460 5461 public void reset() { 5462 synchronized (mDone) { 5463 mDone.set(false); 5464 } 5465 } 5466 5467 public void waitForCompletion() { 5468 synchronized (mDone) { 5469 while (mDone.get() == false) { 5470 try { 5471 mDone.wait(); 5472 } catch (InterruptedException e) { } 5473 } 5474 } 5475 } 5476 5477 int getResult() { 5478 return mResult; 5479 } 5480 5481 @Override 5482 public void packageInstalled(String packageName, int returnCode) 5483 throws RemoteException { 5484 synchronized (mDone) { 5485 mResult = returnCode; 5486 mPackageName = packageName; 5487 mDone.set(true); 5488 mDone.notifyAll(); 5489 } 5490 } 5491 } 5492 5493 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 5494 final AtomicBoolean mDone = new AtomicBoolean(); 5495 int mResult; 5496 5497 public void reset() { 5498 synchronized (mDone) { 5499 mDone.set(false); 5500 } 5501 } 5502 5503 public void waitForCompletion() { 5504 synchronized (mDone) { 5505 while (mDone.get() == false) { 5506 try { 5507 mDone.wait(); 5508 } catch (InterruptedException e) { } 5509 } 5510 } 5511 } 5512 5513 @Override 5514 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 5515 synchronized (mDone) { 5516 mResult = returnCode; 5517 mDone.set(true); 5518 mDone.notifyAll(); 5519 } 5520 } 5521 } 5522 5523 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 5524 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 5525 5526 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 5527 boolean okay = true; 5528 5529 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 5530 5531 // The file content is an .apk file. Copy it out to a staging location and 5532 // attempt to install it. 5533 File apkFile = new File(mDataDir, info.packageName); 5534 try { 5535 FileOutputStream apkStream = new FileOutputStream(apkFile); 5536 byte[] buffer = new byte[32 * 1024]; 5537 long size = info.size; 5538 while (size > 0) { 5539 long toRead = (buffer.length < size) ? buffer.length : size; 5540 int didRead = instream.read(buffer, 0, (int)toRead); 5541 if (didRead >= 0) mBytes += didRead; 5542 apkStream.write(buffer, 0, didRead); 5543 size -= didRead; 5544 } 5545 apkStream.close(); 5546 5547 // make sure the installer can read it 5548 apkFile.setReadable(true, false); 5549 5550 // Now install it 5551 Uri packageUri = Uri.fromFile(apkFile); 5552 mInstallObserver.reset(); 5553 mPackageManager.installPackage(packageUri, mInstallObserver, 5554 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 5555 installerPackage); 5556 mInstallObserver.waitForCompletion(); 5557 5558 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 5559 // The only time we continue to accept install of data even if the 5560 // apk install failed is if we had already determined that we could 5561 // accept the data regardless. 5562 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 5563 okay = false; 5564 } 5565 } else { 5566 // Okay, the install succeeded. Make sure it was the right app. 5567 boolean uninstall = false; 5568 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 5569 Slog.w(TAG, "Restore stream claimed to include apk for " 5570 + info.packageName + " but apk was really " 5571 + mInstallObserver.mPackageName); 5572 // delete the package we just put in place; it might be fraudulent 5573 okay = false; 5574 uninstall = true; 5575 } else { 5576 try { 5577 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 5578 PackageManager.GET_SIGNATURES); 5579 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 5580 Slog.w(TAG, "Restore stream contains apk of package " 5581 + info.packageName + " but it disallows backup/restore"); 5582 okay = false; 5583 } else { 5584 // So far so good -- do the signatures match the manifest? 5585 Signature[] sigs = mManifestSignatures.get(info.packageName); 5586 if (signaturesMatch(sigs, pkg)) { 5587 // If this is a system-uid app without a declared backup agent, 5588 // don't restore any of the file data. 5589 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 5590 && (pkg.applicationInfo.backupAgentName == null)) { 5591 Slog.w(TAG, "Installed app " + info.packageName 5592 + " has restricted uid and no agent"); 5593 okay = false; 5594 } 5595 } else { 5596 Slog.w(TAG, "Installed app " + info.packageName 5597 + " signatures do not match restore manifest"); 5598 okay = false; 5599 uninstall = true; 5600 } 5601 } 5602 } catch (NameNotFoundException e) { 5603 Slog.w(TAG, "Install of package " + info.packageName 5604 + " succeeded but now not found"); 5605 okay = false; 5606 } 5607 } 5608 5609 // If we're not okay at this point, we need to delete the package 5610 // that we just installed. 5611 if (uninstall) { 5612 mDeleteObserver.reset(); 5613 mPackageManager.deletePackage(mInstallObserver.mPackageName, 5614 mDeleteObserver, 0); 5615 mDeleteObserver.waitForCompletion(); 5616 } 5617 } 5618 } catch (IOException e) { 5619 Slog.e(TAG, "Unable to transcribe restored apk for install"); 5620 okay = false; 5621 } finally { 5622 apkFile.delete(); 5623 } 5624 5625 return okay; 5626 } 5627 5628 // Given an actual file content size, consume the post-content padding mandated 5629 // by the tar format. 5630 void skipTarPadding(long size, InputStream instream) throws IOException { 5631 long partial = (size + 512) % 512; 5632 if (partial > 0) { 5633 final int needed = 512 - (int)partial; 5634 byte[] buffer = new byte[needed]; 5635 if (readExactly(instream, buffer, 0, needed) == needed) { 5636 mBytes += needed; 5637 } else throw new IOException("Unexpected EOF in padding"); 5638 } 5639 } 5640 5641 // Read a widget metadata file, returning the restored blob 5642 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 5643 // Fail on suspiciously large widget dump files 5644 if (info.size > 64 * 1024) { 5645 throw new IOException("Metadata too big; corrupt? size=" + info.size); 5646 } 5647 5648 byte[] buffer = new byte[(int) info.size]; 5649 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5650 mBytes += info.size; 5651 } else throw new IOException("Unexpected EOF in widget data"); 5652 5653 String[] str = new String[1]; 5654 int offset = extractLine(buffer, 0, str); 5655 int version = Integer.parseInt(str[0]); 5656 if (version == BACKUP_MANIFEST_VERSION) { 5657 offset = extractLine(buffer, offset, str); 5658 final String pkg = str[0]; 5659 if (info.packageName.equals(pkg)) { 5660 // Data checks out -- the rest of the buffer is a concatenation of 5661 // binary blobs as described in the comment at writeAppWidgetData() 5662 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 5663 offset, buffer.length - offset); 5664 DataInputStream in = new DataInputStream(bin); 5665 while (bin.available() > 0) { 5666 int token = in.readInt(); 5667 int size = in.readInt(); 5668 if (size > 64 * 1024) { 5669 throw new IOException("Datum " 5670 + Integer.toHexString(token) 5671 + " too big; corrupt? size=" + info.size); 5672 } 5673 switch (token) { 5674 case BACKUP_WIDGET_METADATA_TOKEN: 5675 { 5676 if (MORE_DEBUG) { 5677 Slog.i(TAG, "Got widget metadata for " + info.packageName); 5678 } 5679 mWidgetData = new byte[size]; 5680 in.read(mWidgetData); 5681 break; 5682 } 5683 default: 5684 { 5685 if (DEBUG) { 5686 Slog.i(TAG, "Ignoring metadata blob " 5687 + Integer.toHexString(token) 5688 + " for " + info.packageName); 5689 } 5690 in.skipBytes(size); 5691 break; 5692 } 5693 } 5694 } 5695 } else { 5696 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 5697 + " but widget data for " + pkg); 5698 } 5699 } else { 5700 Slog.w(TAG, "Unsupported metadata version " + version); 5701 } 5702 } 5703 5704 // Returns a policy constant; takes a buffer arg to reduce memory churn 5705 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 5706 throws IOException { 5707 // Fail on suspiciously large manifest files 5708 if (info.size > 64 * 1024) { 5709 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 5710 } 5711 5712 byte[] buffer = new byte[(int) info.size]; 5713 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5714 mBytes += info.size; 5715 } else throw new IOException("Unexpected EOF in manifest"); 5716 5717 RestorePolicy policy = RestorePolicy.IGNORE; 5718 String[] str = new String[1]; 5719 int offset = 0; 5720 5721 try { 5722 offset = extractLine(buffer, offset, str); 5723 int version = Integer.parseInt(str[0]); 5724 if (version == BACKUP_MANIFEST_VERSION) { 5725 offset = extractLine(buffer, offset, str); 5726 String manifestPackage = str[0]; 5727 // TODO: handle <original-package> 5728 if (manifestPackage.equals(info.packageName)) { 5729 offset = extractLine(buffer, offset, str); 5730 version = Integer.parseInt(str[0]); // app version 5731 offset = extractLine(buffer, offset, str); 5732 int platformVersion = Integer.parseInt(str[0]); 5733 offset = extractLine(buffer, offset, str); 5734 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 5735 offset = extractLine(buffer, offset, str); 5736 boolean hasApk = str[0].equals("1"); 5737 offset = extractLine(buffer, offset, str); 5738 int numSigs = Integer.parseInt(str[0]); 5739 if (numSigs > 0) { 5740 Signature[] sigs = new Signature[numSigs]; 5741 for (int i = 0; i < numSigs; i++) { 5742 offset = extractLine(buffer, offset, str); 5743 sigs[i] = new Signature(str[0]); 5744 } 5745 mManifestSignatures.put(info.packageName, sigs); 5746 5747 // Okay, got the manifest info we need... 5748 try { 5749 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 5750 info.packageName, PackageManager.GET_SIGNATURES); 5751 // Fall through to IGNORE if the app explicitly disallows backup 5752 final int flags = pkgInfo.applicationInfo.flags; 5753 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 5754 // Restore system-uid-space packages only if they have 5755 // defined a custom backup agent 5756 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 5757 || (pkgInfo.applicationInfo.backupAgentName != null)) { 5758 // Verify signatures against any installed version; if they 5759 // don't match, then we fall though and ignore the data. The 5760 // signatureMatch() method explicitly ignores the signature 5761 // check for packages installed on the system partition, because 5762 // such packages are signed with the platform cert instead of 5763 // the app developer's cert, so they're different on every 5764 // device. 5765 if (signaturesMatch(sigs, pkgInfo)) { 5766 if (pkgInfo.versionCode >= version) { 5767 Slog.i(TAG, "Sig + version match; taking data"); 5768 policy = RestorePolicy.ACCEPT; 5769 } else { 5770 // The data is from a newer version of the app than 5771 // is presently installed. That means we can only 5772 // use it if the matching apk is also supplied. 5773 Slog.d(TAG, "Data version " + version 5774 + " is newer than installed version " 5775 + pkgInfo.versionCode + " - requiring apk"); 5776 policy = RestorePolicy.ACCEPT_IF_APK; 5777 } 5778 } else { 5779 Slog.w(TAG, "Restore manifest signatures do not match " 5780 + "installed application for " + info.packageName); 5781 } 5782 } else { 5783 Slog.w(TAG, "Package " + info.packageName 5784 + " is system level with no agent"); 5785 } 5786 } else { 5787 if (DEBUG) Slog.i(TAG, "Restore manifest from " 5788 + info.packageName + " but allowBackup=false"); 5789 } 5790 } catch (NameNotFoundException e) { 5791 // Okay, the target app isn't installed. We can process 5792 // the restore properly only if the dataset provides the 5793 // apk file and we can successfully install it. 5794 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 5795 + " not installed; requiring apk in dataset"); 5796 policy = RestorePolicy.ACCEPT_IF_APK; 5797 } 5798 5799 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 5800 Slog.i(TAG, "Cannot restore package " + info.packageName 5801 + " without the matching .apk"); 5802 } 5803 } else { 5804 Slog.i(TAG, "Missing signature on backed-up package " 5805 + info.packageName); 5806 } 5807 } else { 5808 Slog.i(TAG, "Expected package " + info.packageName 5809 + " but restore manifest claims " + manifestPackage); 5810 } 5811 } else { 5812 Slog.i(TAG, "Unknown restore manifest version " + version 5813 + " for package " + info.packageName); 5814 } 5815 } catch (NumberFormatException e) { 5816 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 5817 } catch (IllegalArgumentException e) { 5818 Slog.w(TAG, e.getMessage()); 5819 } 5820 5821 return policy; 5822 } 5823 5824 // Builds a line from a byte buffer starting at 'offset', and returns 5825 // the index of the next unconsumed data in the buffer. 5826 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 5827 final int end = buffer.length; 5828 if (offset >= end) throw new IOException("Incomplete data"); 5829 5830 int pos; 5831 for (pos = offset; pos < end; pos++) { 5832 byte c = buffer[pos]; 5833 // at LF we declare end of line, and return the next char as the 5834 // starting point for the next time through 5835 if (c == '\n') { 5836 break; 5837 } 5838 } 5839 outStr[0] = new String(buffer, offset, pos - offset); 5840 pos++; // may be pointing an extra byte past the end but that's okay 5841 return pos; 5842 } 5843 5844 void dumpFileMetadata(FileMetadata info) { 5845 if (DEBUG) { 5846 StringBuilder b = new StringBuilder(128); 5847 5848 // mode string 5849 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 5850 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 5851 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 5852 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 5853 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 5854 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 5855 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 5856 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 5857 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 5858 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 5859 b.append(String.format(" %9d ", info.size)); 5860 5861 Date stamp = new Date(info.mtime); 5862 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 5863 5864 b.append(info.packageName); 5865 b.append(" :: "); 5866 b.append(info.domain); 5867 b.append(" :: "); 5868 b.append(info.path); 5869 5870 Slog.i(TAG, b.toString()); 5871 } 5872 } 5873 // Consume a tar file header block [sequence] and accumulate the relevant metadata 5874 FileMetadata readTarHeaders(InputStream instream) throws IOException { 5875 byte[] block = new byte[512]; 5876 FileMetadata info = null; 5877 5878 boolean gotHeader = readTarHeader(instream, block); 5879 if (gotHeader) { 5880 try { 5881 // okay, presume we're okay, and extract the various metadata 5882 info = new FileMetadata(); 5883 info.size = extractRadix(block, 124, 12, 8); 5884 info.mtime = extractRadix(block, 136, 12, 8); 5885 info.mode = extractRadix(block, 100, 8, 8); 5886 5887 info.path = extractString(block, 345, 155); // prefix 5888 String path = extractString(block, 0, 100); 5889 if (path.length() > 0) { 5890 if (info.path.length() > 0) info.path += '/'; 5891 info.path += path; 5892 } 5893 5894 // tar link indicator field: 1 byte at offset 156 in the header. 5895 int typeChar = block[156]; 5896 if (typeChar == 'x') { 5897 // pax extended header, so we need to read that 5898 gotHeader = readPaxExtendedHeader(instream, info); 5899 if (gotHeader) { 5900 // and after a pax extended header comes another real header -- read 5901 // that to find the real file type 5902 gotHeader = readTarHeader(instream, block); 5903 } 5904 if (!gotHeader) throw new IOException("Bad or missing pax header"); 5905 5906 typeChar = block[156]; 5907 } 5908 5909 switch (typeChar) { 5910 case '0': info.type = BackupAgent.TYPE_FILE; break; 5911 case '5': { 5912 info.type = BackupAgent.TYPE_DIRECTORY; 5913 if (info.size != 0) { 5914 Slog.w(TAG, "Directory entry with nonzero size in header"); 5915 info.size = 0; 5916 } 5917 break; 5918 } 5919 case 0: { 5920 // presume EOF 5921 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 5922 return null; 5923 } 5924 default: { 5925 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 5926 throw new IOException("Unknown entity type " + typeChar); 5927 } 5928 } 5929 5930 // Parse out the path 5931 // 5932 // first: apps/shared/unrecognized 5933 if (FullBackup.SHARED_PREFIX.regionMatches(0, 5934 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 5935 // File in shared storage. !!! TODO: implement this. 5936 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 5937 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 5938 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 5939 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 5940 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 5941 info.path, 0, FullBackup.APPS_PREFIX.length())) { 5942 // App content! Parse out the package name and domain 5943 5944 // strip the apps/ prefix 5945 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 5946 5947 // extract the package name 5948 int slash = info.path.indexOf('/'); 5949 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 5950 info.packageName = info.path.substring(0, slash); 5951 info.path = info.path.substring(slash+1); 5952 5953 // if it's a manifest we're done, otherwise parse out the domains 5954 if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) { 5955 slash = info.path.indexOf('/'); 5956 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path); 5957 info.domain = info.path.substring(0, slash); 5958 info.path = info.path.substring(slash + 1); 5959 } 5960 } 5961 } catch (IOException e) { 5962 if (DEBUG) { 5963 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 5964 HEXLOG(block); 5965 } 5966 throw e; 5967 } 5968 } 5969 return info; 5970 } 5971 5972 private void HEXLOG(byte[] block) { 5973 int offset = 0; 5974 int todo = block.length; 5975 StringBuilder buf = new StringBuilder(64); 5976 while (todo > 0) { 5977 buf.append(String.format("%04x ", offset)); 5978 int numThisLine = (todo > 16) ? 16 : todo; 5979 for (int i = 0; i < numThisLine; i++) { 5980 buf.append(String.format("%02x ", block[offset+i])); 5981 } 5982 Slog.i("hexdump", buf.toString()); 5983 buf.setLength(0); 5984 todo -= numThisLine; 5985 offset += numThisLine; 5986 } 5987 } 5988 5989 // Read exactly the given number of bytes into a buffer at the stated offset. 5990 // Returns false if EOF is encountered before the requested number of bytes 5991 // could be read. 5992 int readExactly(InputStream in, byte[] buffer, int offset, int size) 5993 throws IOException { 5994 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 5995 5996 int soFar = 0; 5997 while (soFar < size) { 5998 int nRead = in.read(buffer, offset + soFar, size - soFar); 5999 if (nRead <= 0) { 6000 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 6001 break; 6002 } 6003 soFar += nRead; 6004 } 6005 return soFar; 6006 } 6007 6008 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 6009 final int got = readExactly(instream, block, 0, 512); 6010 if (got == 0) return false; // Clean EOF 6011 if (got < 512) throw new IOException("Unable to read full block header"); 6012 mBytes += 512; 6013 return true; 6014 } 6015 6016 // overwrites 'info' fields based on the pax extended header 6017 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 6018 throws IOException { 6019 // We should never see a pax extended header larger than this 6020 if (info.size > 32*1024) { 6021 Slog.w(TAG, "Suspiciously large pax header size " + info.size 6022 + " - aborting"); 6023 throw new IOException("Sanity failure: pax header size " + info.size); 6024 } 6025 6026 // read whole blocks, not just the content size 6027 int numBlocks = (int)((info.size + 511) >> 9); 6028 byte[] data = new byte[numBlocks * 512]; 6029 if (readExactly(instream, data, 0, data.length) < data.length) { 6030 throw new IOException("Unable to read full pax header"); 6031 } 6032 mBytes += data.length; 6033 6034 final int contentSize = (int) info.size; 6035 int offset = 0; 6036 do { 6037 // extract the line at 'offset' 6038 int eol = offset+1; 6039 while (eol < contentSize && data[eol] != ' ') eol++; 6040 if (eol >= contentSize) { 6041 // error: we just hit EOD looking for the end of the size field 6042 throw new IOException("Invalid pax data"); 6043 } 6044 // eol points to the space between the count and the key 6045 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 6046 int key = eol + 1; // start of key=value 6047 eol = offset + linelen - 1; // trailing LF 6048 int value; 6049 for (value = key+1; data[value] != '=' && value <= eol; value++); 6050 if (value > eol) { 6051 throw new IOException("Invalid pax declaration"); 6052 } 6053 6054 // pax requires that key/value strings be in UTF-8 6055 String keyStr = new String(data, key, value-key, "UTF-8"); 6056 // -1 to strip the trailing LF 6057 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 6058 6059 if ("path".equals(keyStr)) { 6060 info.path = valStr; 6061 } else if ("size".equals(keyStr)) { 6062 info.size = Long.parseLong(valStr); 6063 } else { 6064 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 6065 } 6066 6067 offset += linelen; 6068 } while (offset < contentSize); 6069 6070 return true; 6071 } 6072 6073 long extractRadix(byte[] data, int offset, int maxChars, int radix) 6074 throws IOException { 6075 long value = 0; 6076 final int end = offset + maxChars; 6077 for (int i = offset; i < end; i++) { 6078 final byte b = data[i]; 6079 // Numeric fields in tar can terminate with either NUL or SPC 6080 if (b == 0 || b == ' ') break; 6081 if (b < '0' || b > ('0' + radix - 1)) { 6082 throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix); 6083 } 6084 value = radix * value + (b - '0'); 6085 } 6086 return value; 6087 } 6088 6089 String extractString(byte[] data, int offset, int maxChars) throws IOException { 6090 final int end = offset + maxChars; 6091 int eos = offset; 6092 // tar string fields terminate early with a NUL 6093 while (eos < end && data[eos] != 0) eos++; 6094 return new String(data, offset, eos-offset, "US-ASCII"); 6095 } 6096 6097 void sendStartRestore() { 6098 if (mObserver != null) { 6099 try { 6100 mObserver.onStartRestore(); 6101 } catch (RemoteException e) { 6102 Slog.w(TAG, "full restore observer went away: startRestore"); 6103 mObserver = null; 6104 } 6105 } 6106 } 6107 6108 void sendOnRestorePackage(String name) { 6109 if (mObserver != null) { 6110 try { 6111 // TODO: use a more user-friendly name string 6112 mObserver.onRestorePackage(name); 6113 } catch (RemoteException e) { 6114 Slog.w(TAG, "full restore observer went away: restorePackage"); 6115 mObserver = null; 6116 } 6117 } 6118 } 6119 6120 void sendEndRestore() { 6121 if (mObserver != null) { 6122 try { 6123 mObserver.onEndRestore(); 6124 } catch (RemoteException e) { 6125 Slog.w(TAG, "full restore observer went away: endRestore"); 6126 mObserver = null; 6127 } 6128 } 6129 } 6130 } 6131 6132 // ----- Restore handling ----- 6133 6134 // new style: we only store the SHA-1 hashes of each sig, not the full block 6135 static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) { 6136 if (target == null) { 6137 return false; 6138 } 6139 6140 // If the target resides on the system partition, we allow it to restore 6141 // data from the like-named package in a restore set even if the signatures 6142 // do not match. (Unlike general applications, those flashed to the system 6143 // partition will be signed with the device's platform certificate, so on 6144 // different phones the same system app will have different signatures.) 6145 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 6146 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 6147 return true; 6148 } 6149 6150 // Allow unsigned apps, but not signed on one device and unsigned on the other 6151 // !!! TODO: is this the right policy? 6152 Signature[] deviceSigs = target.signatures; 6153 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes 6154 + " device=" + deviceSigs); 6155 if ((storedSigHashes == null || storedSigHashes.size() == 0) 6156 && (deviceSigs == null || deviceSigs.length == 0)) { 6157 return true; 6158 } 6159 if (storedSigHashes == null || deviceSigs == null) { 6160 return false; 6161 } 6162 6163 // !!! TODO: this demands that every stored signature match one 6164 // that is present on device, and does not demand the converse. 6165 // Is this this right policy? 6166 final int nStored = storedSigHashes.size(); 6167 final int nDevice = deviceSigs.length; 6168 6169 // hash each on-device signature 6170 ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice); 6171 for (int i = 0; i < nDevice; i++) { 6172 deviceHashes.add(hashSignature(deviceSigs[i])); 6173 } 6174 6175 // now ensure that each stored sig (hash) matches an on-device sig (hash) 6176 for (int n = 0; n < nStored; n++) { 6177 boolean match = false; 6178 final byte[] storedHash = storedSigHashes.get(n); 6179 for (int i = 0; i < nDevice; i++) { 6180 if (Arrays.equals(storedHash, deviceHashes.get(i))) { 6181 match = true; 6182 break; 6183 } 6184 } 6185 // match is false when no on-device sig matched one of the stored ones 6186 if (!match) { 6187 return false; 6188 } 6189 } 6190 6191 return true; 6192 } 6193 6194 static byte[] hashSignature(Signature sig) { 6195 try { 6196 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 6197 digest.update(sig.toByteArray()); 6198 return digest.digest(); 6199 } catch (NoSuchAlgorithmException e) { 6200 Slog.w(TAG, "No SHA-256 algorithm found!"); 6201 } 6202 return null; 6203 } 6204 6205 // Old style: directly match the stored vs on device signature blocks 6206 static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 6207 if (target == null) { 6208 return false; 6209 } 6210 6211 // If the target resides on the system partition, we allow it to restore 6212 // data from the like-named package in a restore set even if the signatures 6213 // do not match. (Unlike general applications, those flashed to the system 6214 // partition will be signed with the device's platform certificate, so on 6215 // different phones the same system app will have different signatures.) 6216 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 6217 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 6218 return true; 6219 } 6220 6221 // Allow unsigned apps, but not signed on one device and unsigned on the other 6222 // !!! TODO: is this the right policy? 6223 Signature[] deviceSigs = target.signatures; 6224 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs 6225 + " device=" + deviceSigs); 6226 if ((storedSigs == null || storedSigs.length == 0) 6227 && (deviceSigs == null || deviceSigs.length == 0)) { 6228 return true; 6229 } 6230 if (storedSigs == null || deviceSigs == null) { 6231 return false; 6232 } 6233 6234 // !!! TODO: this demands that every stored signature match one 6235 // that is present on device, and does not demand the converse. 6236 // Is this this right policy? 6237 int nStored = storedSigs.length; 6238 int nDevice = deviceSigs.length; 6239 6240 for (int i=0; i < nStored; i++) { 6241 boolean match = false; 6242 for (int j=0; j < nDevice; j++) { 6243 if (storedSigs[i].equals(deviceSigs[j])) { 6244 match = true; 6245 break; 6246 } 6247 } 6248 if (!match) { 6249 return false; 6250 } 6251 } 6252 return true; 6253 } 6254 6255 // Used by both incremental and full restore 6256 void restoreWidgetData(String packageName, byte[] widgetData) { 6257 // Apply the restored widget state and generate the ID update for the app 6258 AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER); 6259 } 6260 6261 // ***************************** 6262 // NEW UNIFIED RESTORE IMPLEMENTATION 6263 // ***************************** 6264 6265 // states of the unified-restore state machine 6266 enum UnifiedRestoreState { 6267 INITIAL, 6268 RUNNING_QUEUE, 6269 RESTORE_KEYVALUE, 6270 RESTORE_FULL, 6271 FINAL 6272 } 6273 6274 class PerformUnifiedRestoreTask implements BackupRestoreTask { 6275 // Transport we're working with to do the restore 6276 private IBackupTransport mTransport; 6277 6278 // Where per-transport saved state goes 6279 File mStateDir; 6280 6281 // Restore observer; may be null 6282 private IRestoreObserver mObserver; 6283 6284 // Token identifying the dataset to the transport 6285 private long mToken; 6286 6287 // When this is a restore-during-install, this is the token identifying the 6288 // operation to the Package Manager, and we must ensure that we let it know 6289 // when we're finished. 6290 private int mPmToken; 6291 6292 // Is this a whole-system restore, i.e. are we establishing a new ancestral 6293 // dataset to base future restore-at-install operations from? 6294 private boolean mIsSystemRestore; 6295 6296 // If this is a single-package restore, what package are we interested in? 6297 private PackageInfo mTargetPackage; 6298 6299 // In all cases, the calculated list of packages that we are trying to restore 6300 private List<PackageInfo> mAcceptSet; 6301 6302 // Our bookkeeping about the ancestral dataset 6303 private PackageManagerBackupAgent mPmAgent; 6304 6305 // What sort of restore we're doing now 6306 private RestoreDescription mRestoreDescription; 6307 6308 // The package we're currently restoring 6309 private PackageInfo mCurrentPackage; 6310 6311 // Widget-related data handled as part of this restore operation 6312 private byte[] mWidgetData; 6313 6314 // Number of apps restored in this pass 6315 private int mCount; 6316 6317 // When did we start? 6318 private long mStartRealtime; 6319 6320 // State machine progress 6321 private UnifiedRestoreState mState; 6322 6323 // How are things going? 6324 private int mStatus; 6325 6326 // Done? 6327 private boolean mFinished; 6328 6329 // Key/value: bookkeeping about staged data and files for agent access 6330 private File mBackupDataName; 6331 private File mStageName; 6332 private File mSavedStateName; 6333 private File mNewStateName; 6334 ParcelFileDescriptor mBackupData; 6335 ParcelFileDescriptor mNewState; 6336 6337 // Invariant: mWakelock is already held, and this task is responsible for 6338 // releasing it at the end of the restore operation. 6339 PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer, 6340 long restoreSetToken, PackageInfo targetPackage, int pmToken, 6341 boolean isFullSystemRestore, String[] filterSet) { 6342 mState = UnifiedRestoreState.INITIAL; 6343 mStartRealtime = SystemClock.elapsedRealtime(); 6344 6345 mTransport = transport; 6346 mObserver = observer; 6347 mToken = restoreSetToken; 6348 mPmToken = pmToken; 6349 mTargetPackage = targetPackage; 6350 mIsSystemRestore = isFullSystemRestore; 6351 mFinished = false; 6352 6353 if (filterSet == null) { 6354 // We want everything and a pony 6355 List<PackageInfo> apps 6356 = PackageManagerBackupAgent.getStorableApplications(mPackageManager); 6357 filterSet = packagesToNames(apps); 6358 if (DEBUG) { 6359 Slog.i(TAG, "Full restore; asking for " + filterSet.length + " apps"); 6360 } 6361 } 6362 6363 mAcceptSet = new ArrayList<PackageInfo>(filterSet.length); 6364 6365 // Pro tem, we insist on moving the settings provider package to last place. 6366 // Keep track of whether it's in the list, and bump it down if so. We also 6367 // want to do the system package itself first if it's called for. 6368 boolean hasSystem = false; 6369 boolean hasSettings = false; 6370 for (int i = 0; i < filterSet.length; i++) { 6371 try { 6372 PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0); 6373 if ("android".equals(info.packageName)) { 6374 hasSystem = true; 6375 continue; 6376 } 6377 if (SETTINGS_PACKAGE.equals(info.packageName)) { 6378 hasSettings = true; 6379 continue; 6380 } 6381 6382 if (appIsEligibleForBackup(info.applicationInfo)) { 6383 mAcceptSet.add(info); 6384 } 6385 } catch (NameNotFoundException e) { 6386 // requested package name doesn't exist; ignore it 6387 } 6388 } 6389 if (hasSystem) { 6390 try { 6391 mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0)); 6392 } catch (NameNotFoundException e) { 6393 // won't happen; we know a priori that it's valid 6394 } 6395 } 6396 if (hasSettings) { 6397 try { 6398 mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0)); 6399 } catch (NameNotFoundException e) { 6400 // this one is always valid too 6401 } 6402 } 6403 } 6404 6405 private String[] packagesToNames(List<PackageInfo> apps) { 6406 final int N = apps.size(); 6407 String[] names = new String[N]; 6408 for (int i = 0; i < N; i++) { 6409 names[i] = apps.get(i).packageName; 6410 } 6411 return names; 6412 } 6413 6414 // Execute one tick of whatever state machine the task implements 6415 @Override 6416 public void execute() { 6417 if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState); 6418 switch (mState) { 6419 case INITIAL: 6420 startRestore(); 6421 break; 6422 6423 case RUNNING_QUEUE: 6424 dispatchNextRestore(); 6425 break; 6426 6427 case RESTORE_KEYVALUE: 6428 restoreKeyValue(); 6429 break; 6430 6431 case RESTORE_FULL: 6432 restoreFull(); 6433 break; 6434 6435 case FINAL: 6436 if (!mFinished) finalizeRestore(); 6437 else { 6438 Slog.e(TAG, "Duplicate finish"); 6439 } 6440 mFinished = true; 6441 break; 6442 } 6443 } 6444 6445 /* 6446 * SKETCH OF OPERATION 6447 * 6448 * create one of these PerformUnifiedRestoreTask objects, telling it which 6449 * dataset & transport to address, and then parameters within the restore 6450 * operation: single target package vs many, etc. 6451 * 6452 * 1. transport.startRestore(token, list-of-packages). If we need @pm@ it is 6453 * always placed first and the settings provider always placed last [for now]. 6454 * 6455 * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline] 6456 * 6457 * [ state change => RUNNING_QUEUE ] 6458 * 6459 * NOW ITERATE: 6460 * 6461 * { 3. t.nextRestorePackage() 6462 * 4. does the metadata for this package allow us to restore it? 6463 * does the on-disk app permit us to restore it? [re-check allowBackup etc] 6464 * 5. is this a key/value dataset? => key/value agent restore 6465 * [ state change => RESTORE_KEYVALUE ] 6466 * 5a. spin up agent 6467 * 5b. t.getRestoreData() to stage it properly 6468 * 5c. call into agent to perform restore 6469 * 5d. tear down agent 6470 * [ state change => RUNNING_QUEUE ] 6471 * 6472 * 6. else it's a stream dataset: 6473 * [ state change => RESTORE_FULL ] 6474 * 6a. instantiate the engine for a stream restore: engine handles agent lifecycles 6475 * 6b. spin off engine runner on separate thread 6476 * 6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket 6477 * [ state change => RUNNING_QUEUE ] 6478 * } 6479 * 6480 * [ state change => FINAL ] 6481 * 6482 * 7. t.finishRestore(), release wakelock, etc. 6483 * 6484 * 6485 */ 6486 6487 // state INITIAL : set up for the restore and read the metadata if necessary 6488 private void startRestore() { 6489 sendStartRestore(mAcceptSet.size()); 6490 6491 UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE; 6492 try { 6493 // If we don't yet have PM metadata for this token, synthesize an 6494 // entry for the PM pseudopackage and make it the first to be 6495 // restored. 6496 String transportDir = mTransport.transportDirName(); 6497 mStateDir = new File(mBaseStateDir, transportDir); 6498 File metadataDir = new File(mStateDir, "_metadata"); 6499 metadataDir.mkdirs(); 6500 File metadataFile = new File(metadataDir, Long.toHexString(mToken)); 6501 try { 6502 // PM info is cached in $BASE/transport/_metadata/$TOKEN 6503 mPmAgent = new PackageManagerBackupAgent(metadataFile); 6504 } catch (IOException e) { 6505 // Nope, we need to get it via restore 6506 PackageInfo pmPackage = new PackageInfo(); 6507 pmPackage.packageName = PACKAGE_MANAGER_SENTINEL; 6508 mAcceptSet.add(0, pmPackage); 6509 } 6510 6511 PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]); 6512 mStatus = mTransport.startRestore(mToken, packages); 6513 if (mStatus != BackupTransport.TRANSPORT_OK) { 6514 Slog.e(TAG, "Transport error " + mStatus + "; no restore possible"); 6515 mStatus = BackupTransport.TRANSPORT_ERROR; 6516 nextState = UnifiedRestoreState.FINAL; 6517 return; 6518 } 6519 6520 if (mPmAgent == null) { 6521 if (DEBUG) { 6522 Slog.v(TAG, "Need to fetch metadata for token " 6523 + Long.toHexString(mToken)); 6524 } 6525 RestoreDescription desc = mTransport.nextRestorePackage(); 6526 if (desc == null) { 6527 Slog.e(TAG, "No restore metadata available; halting"); 6528 mStatus = BackupTransport.TRANSPORT_ERROR; 6529 nextState = UnifiedRestoreState.FINAL; 6530 return; 6531 } 6532 if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) { 6533 Slog.e(TAG, "Required metadata but got " + desc.getPackageName()); 6534 mStatus = BackupTransport.TRANSPORT_ERROR; 6535 nextState = UnifiedRestoreState.FINAL; 6536 return; 6537 } 6538 6539 // Pull the Package Manager metadata from the restore set first 6540 PackageInfo omPackage = new PackageInfo(); 6541 omPackage.packageName = PACKAGE_MANAGER_SENTINEL; 6542 mPmAgent = new PackageManagerBackupAgent(mPackageManager, null); 6543 initiateOneRestore(omPackage, 0, 6544 IBackupAgent.Stub.asInterface(mPmAgent.onBind())); 6545 // The PM agent called operationComplete() already, because our invocation 6546 // of it is process-local and therefore synchronous. That means that a 6547 // RUNNING_QUEUE message is already enqueued. Only if we're unable to 6548 // proceed with running the queue do we remove that pending message and 6549 // jump straight to the FINAL state. 6550 6551 // Verify that the backup set includes metadata. If not, we can't do 6552 // signature/version verification etc, so we simply do not proceed with 6553 // the restore operation. 6554 if (!mPmAgent.hasMetadata()) { 6555 Slog.e(TAG, "No restore metadata available, so not restoring settings"); 6556 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 6557 PACKAGE_MANAGER_SENTINEL, 6558 "Package manager restore metadata missing"); 6559 mStatus = BackupTransport.TRANSPORT_ERROR; 6560 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 6561 nextState = UnifiedRestoreState.FINAL; 6562 return; 6563 } 6564 6565 // Success; cache the metadata and continue as expected with the 6566 // RUNNING_QUEUE step already enqueued. 6567 if (DEBUG) { 6568 Slog.v(TAG, "Got metadata; caching and proceeding to restore"); 6569 } 6570 try { 6571 mPmAgent.saveToDisk(metadataFile); 6572 } catch (IOException e) { 6573 // Something bad; we need to abort 6574 Slog.e(TAG, "Unable to write restored metadata"); 6575 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 6576 PACKAGE_MANAGER_SENTINEL, 6577 "Unable to write restored metadata"); 6578 mStatus = BackupTransport.TRANSPORT_ERROR; 6579 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 6580 nextState = UnifiedRestoreState.FINAL; 6581 return; 6582 } 6583 } 6584 } catch (RemoteException e) { 6585 // If we lost the transport at any time, halt 6586 Slog.e(TAG, "Unable to contact transport for restore"); 6587 mStatus = BackupTransport.TRANSPORT_ERROR; 6588 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 6589 nextState = UnifiedRestoreState.FINAL; 6590 return; 6591 } finally { 6592 executeNextState(nextState); 6593 } 6594 } 6595 6596 // state RUNNING_QUEUE : figure out what the next thing to be restored is, 6597 // and fire the appropriate next step 6598 private void dispatchNextRestore() { 6599 UnifiedRestoreState nextState = UnifiedRestoreState.FINAL; 6600 try { 6601 mRestoreDescription = mTransport.nextRestorePackage(); 6602 final int type = mRestoreDescription.getDataType(); 6603 final String pkgName = mRestoreDescription.getPackageName(); 6604 if (pkgName == null) { 6605 Slog.e(TAG, "Failure getting next package name"); 6606 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 6607 nextState = UnifiedRestoreState.FINAL; 6608 return; 6609 } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) { 6610 // Yay we've reached the end cleanly 6611 if (DEBUG) { 6612 Slog.v(TAG, "No more packages; finishing restore"); 6613 } 6614 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); 6615 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis); 6616 nextState = UnifiedRestoreState.FINAL; 6617 return; 6618 } 6619 6620 if (DEBUG) { 6621 Slog.i(TAG, "Next restore package: " + mRestoreDescription); 6622 } 6623 sendOnRestorePackage(pkgName); 6624 6625 Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName); 6626 if (metaInfo == null) { 6627 Slog.e(TAG, "No metadata for " + pkgName); 6628 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 6629 "Package metadata missing"); 6630 nextState = UnifiedRestoreState.RUNNING_QUEUE; 6631 return; 6632 } 6633 6634 try { 6635 mCurrentPackage = mPackageManager.getPackageInfo( 6636 pkgName, PackageManager.GET_SIGNATURES); 6637 } catch (NameNotFoundException e) { 6638 // Whoops, we thought we could restore this package but it 6639 // turns out not to be present. Skip it. 6640 Slog.e(TAG, "Package not present: " + pkgName); 6641 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 6642 "Package missing on device"); 6643 nextState = UnifiedRestoreState.RUNNING_QUEUE; 6644 return; 6645 } 6646 6647 if (metaInfo.versionCode > mCurrentPackage.versionCode) { 6648 // Data is from a "newer" version of the app than we have currently 6649 // installed. If the app has not declared that it is prepared to 6650 // handle this case, we do not attempt the restore. 6651 if ((mCurrentPackage.applicationInfo.flags 6652 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { 6653 String message = "Version " + metaInfo.versionCode 6654 + " > installed version " + mCurrentPackage.versionCode; 6655 Slog.w(TAG, "Package " + pkgName + ": " + message); 6656 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 6657 pkgName, message); 6658 nextState = UnifiedRestoreState.RUNNING_QUEUE; 6659 return; 6660 } else { 6661 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode 6662 + " > installed " + mCurrentPackage.versionCode 6663 + " but restoreAnyVersion"); 6664 } 6665 } 6666 6667 if (DEBUG) Slog.v(TAG, "Package " + pkgName 6668 + " restore version [" + metaInfo.versionCode 6669 + "] is compatible with installed version [" 6670 + mCurrentPackage.versionCode + "]"); 6671 6672 // Reset per-package preconditions and fire the appropriate next state 6673 mWidgetData = null; 6674 if (type == RestoreDescription.TYPE_KEY_VALUE) { 6675 nextState = UnifiedRestoreState.RESTORE_KEYVALUE; 6676 } else if (type == RestoreDescription.TYPE_FULL_STREAM) { 6677 nextState = UnifiedRestoreState.RESTORE_FULL; 6678 } else { 6679 // Unknown restore type; ignore this package and move on 6680 Slog.e(TAG, "Unrecognized restore type " + type); 6681 nextState = UnifiedRestoreState.RUNNING_QUEUE; 6682 return; 6683 } 6684 } catch (RemoteException e) { 6685 Slog.e(TAG, "Can't get next target from transport; ending restore"); 6686 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 6687 nextState = UnifiedRestoreState.FINAL; 6688 return; 6689 } finally { 6690 executeNextState(nextState); 6691 } 6692 } 6693 6694 // state RESTORE_KEYVALUE : restore one package via key/value API set 6695 private void restoreKeyValue() { 6696 // Initiating the restore will pass responsibility for the state machine's 6697 // progress to the agent callback, so we do not always execute the 6698 // next state here. 6699 final String packageName = mCurrentPackage.packageName; 6700 // Validate some semantic requirements that apply in this way 6701 // only to the key/value restore API flow 6702 if (mCurrentPackage.applicationInfo.backupAgentName == null 6703 || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) { 6704 if (DEBUG) { 6705 Slog.i(TAG, "Data exists for package " + packageName 6706 + " but app has no agent; skipping"); 6707 } 6708 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 6709 "Package has no agent"); 6710 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 6711 return; 6712 } 6713 6714 Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); 6715 if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { 6716 Slog.w(TAG, "Signature mismatch restoring " + packageName); 6717 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 6718 "Signature mismatch"); 6719 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 6720 return; 6721 } 6722 6723 // Good to go! Set up and bind the agent... 6724 IBackupAgent agent = bindToAgentSynchronous( 6725 mCurrentPackage.applicationInfo, 6726 IApplicationThread.BACKUP_MODE_INCREMENTAL); 6727 if (agent == null) { 6728 Slog.w(TAG, "Can't find backup agent for " + packageName); 6729 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 6730 "Restore agent missing"); 6731 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 6732 return; 6733 } 6734 6735 // And then finally start the restore on this agent 6736 try { 6737 initiateOneRestore(mCurrentPackage, metaInfo.versionCode, agent); 6738 ++mCount; 6739 } catch (Exception e) { 6740 Slog.e(TAG, "Error when attempting restore: " + e.toString()); 6741 keyValueAgentErrorCleanup(); 6742 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 6743 } 6744 } 6745 6746 // Guts of a key/value restore operation 6747 void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) { 6748 final String packageName = app.packageName; 6749 6750 if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName); 6751 6752 // !!! TODO: get the dirs from the transport 6753 mBackupDataName = new File(mDataDir, packageName + ".restore"); 6754 mStageName = new File(mDataDir, packageName + ".stage"); 6755 mNewStateName = new File(mStateDir, packageName + ".new"); 6756 mSavedStateName = new File(mStateDir, packageName); 6757 6758 // don't stage the 'android' package where the wallpaper data lives. this is 6759 // an optimization: we know there's no widget data hosted/published by that 6760 // package, and this way we avoid doing a spurious copy of MB-sized wallpaper 6761 // data following the download. 6762 boolean staging = !packageName.equals("android"); 6763 ParcelFileDescriptor stage; 6764 File downloadFile = (staging) ? mStageName : mBackupDataName; 6765 6766 final int token = generateToken(); 6767 try { 6768 // Run the transport's restore pass 6769 stage = ParcelFileDescriptor.open(downloadFile, 6770 ParcelFileDescriptor.MODE_READ_WRITE | 6771 ParcelFileDescriptor.MODE_CREATE | 6772 ParcelFileDescriptor.MODE_TRUNCATE); 6773 6774 if (!SELinux.restorecon(mBackupDataName)) { 6775 Slog.e(TAG, "SElinux restorecon failed for " + downloadFile); 6776 } 6777 6778 if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) { 6779 // Transport-level failure, so we wind everything up and 6780 // terminate the restore operation. 6781 Slog.e(TAG, "Error getting restore data for " + packageName); 6782 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 6783 stage.close(); 6784 downloadFile.delete(); 6785 executeNextState(UnifiedRestoreState.FINAL); 6786 return; 6787 } 6788 6789 // We have the data from the transport. Now we extract and strip 6790 // any per-package metadata (typically widget-related information) 6791 // if appropriate 6792 if (staging) { 6793 stage.close(); 6794 stage = ParcelFileDescriptor.open(downloadFile, 6795 ParcelFileDescriptor.MODE_READ_ONLY); 6796 6797 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 6798 ParcelFileDescriptor.MODE_READ_WRITE | 6799 ParcelFileDescriptor.MODE_CREATE | 6800 ParcelFileDescriptor.MODE_TRUNCATE); 6801 6802 BackupDataInput in = new BackupDataInput(stage.getFileDescriptor()); 6803 BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor()); 6804 byte[] buffer = new byte[8192]; // will grow when needed 6805 while (in.readNextHeader()) { 6806 final String key = in.getKey(); 6807 final int size = in.getDataSize(); 6808 6809 // is this a special key? 6810 if (key.equals(KEY_WIDGET_STATE)) { 6811 if (DEBUG) { 6812 Slog.i(TAG, "Restoring widget state for " + packageName); 6813 } 6814 mWidgetData = new byte[size]; 6815 in.readEntityData(mWidgetData, 0, size); 6816 } else { 6817 if (size > buffer.length) { 6818 buffer = new byte[size]; 6819 } 6820 in.readEntityData(buffer, 0, size); 6821 out.writeEntityHeader(key, size); 6822 out.writeEntityData(buffer, size); 6823 } 6824 } 6825 6826 mBackupData.close(); 6827 } 6828 6829 // Okay, we have the data. Now have the agent do the restore. 6830 stage.close(); 6831 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 6832 ParcelFileDescriptor.MODE_READ_ONLY); 6833 6834 mNewState = ParcelFileDescriptor.open(mNewStateName, 6835 ParcelFileDescriptor.MODE_READ_WRITE | 6836 ParcelFileDescriptor.MODE_CREATE | 6837 ParcelFileDescriptor.MODE_TRUNCATE); 6838 6839 // Kick off the restore, checking for hung agents. The timeout or 6840 // the operationComplete() callback will schedule the next step, 6841 // so we do not do that here. 6842 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this); 6843 agent.doRestore(mBackupData, appVersionCode, mNewState, 6844 token, mBackupManagerBinder); 6845 } catch (Exception e) { 6846 Slog.e(TAG, "Unable to call app for restore: " + packageName, e); 6847 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 6848 packageName, e.toString()); 6849 keyValueAgentErrorCleanup(); // clears any pending timeout messages as well 6850 6851 // After a restore failure we go back to running the queue. If there 6852 // are no more packages to be restored that will be handled by the 6853 // next step. 6854 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 6855 } 6856 } 6857 6858 // state RESTORE_FULL : restore one package via streaming engine 6859 private void restoreFull() { 6860 // None of this can run on the work looper here, so we spin asynchronous 6861 // work like this: 6862 // 6863 // StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk() 6864 // write it into the pipe to the engine 6865 // EngineThread: FullRestoreEngine thread communicating with the target app 6866 // 6867 // When finished, StreamFeederThread executes next state as appropriate on the 6868 // backup looper, and the overall unified restore task resumes 6869 try { 6870 StreamFeederThread feeder = new StreamFeederThread(); 6871 if (DEBUG) { 6872 Slog.i(TAG, "Spinning threads for stream restore of " 6873 + mCurrentPackage.packageName); 6874 } 6875 new Thread(feeder, "unified-stream-feeder").start(); 6876 6877 // At this point the feeder is responsible for advancing the restore 6878 // state, so we're done here. 6879 } catch (IOException e) { 6880 // Unable to instantiate the feeder thread -- we need to bail on the 6881 // current target. We haven't asked the transport for data yet, though, 6882 // so we can do that simply by going back to running the restore queue. 6883 Slog.e(TAG, "Unable to construct pipes for stream restore!"); 6884 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 6885 } 6886 } 6887 6888 class StreamFeederThread extends RestoreEngine implements Runnable { 6889 final String TAG = "StreamFeederThread"; 6890 FullRestoreEngine mEngine; 6891 6892 // pipe through which we read data from the transport. [0] read, [1] write 6893 ParcelFileDescriptor[] mTransportPipes; 6894 6895 // pipe through which the engine will read data. [0] read, [1] write 6896 ParcelFileDescriptor[] mEnginePipes; 6897 6898 public StreamFeederThread() throws IOException { 6899 mTransportPipes = ParcelFileDescriptor.createPipe(); 6900 mEnginePipes = ParcelFileDescriptor.createPipe(); 6901 setRunning(true); 6902 } 6903 6904 @Override 6905 public void run() { 6906 UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE; 6907 int status = BackupTransport.TRANSPORT_OK; 6908 6909 mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false); 6910 EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]); 6911 6912 ParcelFileDescriptor eWriteEnd = mEnginePipes[1]; 6913 ParcelFileDescriptor tReadEnd = mTransportPipes[0]; 6914 ParcelFileDescriptor tWriteEnd = mTransportPipes[1]; 6915 6916 int bufferSize = 32 * 1024; 6917 byte[] buffer = new byte[bufferSize]; 6918 FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor()); 6919 FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor()); 6920 6921 // spin up the engine and start moving data to it 6922 new Thread(eThread, "unified-restore-engine").start(); 6923 6924 try { 6925 while (status == BackupTransport.TRANSPORT_OK) { 6926 // have the transport write some of the restoring data to us 6927 int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd); 6928 if (result > 0) { 6929 // The transport wrote this many bytes of restore data to the 6930 // pipe, so pass it along to the engine. 6931 if (MORE_DEBUG) { 6932 Slog.v(TAG, " <- transport provided chunk size " + result); 6933 } 6934 if (result > bufferSize) { 6935 bufferSize = result; 6936 buffer = new byte[bufferSize]; 6937 } 6938 int toCopy = result; 6939 while (toCopy > 0) { 6940 int n = transportIn.read(buffer, 0, toCopy); 6941 engineOut.write(buffer, 0, n); 6942 toCopy -= n; 6943 if (MORE_DEBUG) { 6944 Slog.v(TAG, " -> wrote " + n + " to engine, left=" + toCopy); 6945 } 6946 } 6947 } else if (result == BackupTransport.NO_MORE_DATA) { 6948 // Clean finish. Wind up and we're done! 6949 if (MORE_DEBUG) { 6950 Slog.i(TAG, "Got clean full-restore EOF for " 6951 + mCurrentPackage.packageName); 6952 } 6953 status = BackupTransport.TRANSPORT_OK; 6954 break; 6955 } else { 6956 // Transport reported some sort of failure; the fall-through 6957 // handling will deal properly with that. 6958 Slog.e(TAG, "Error " + result + " streaming restore for " 6959 + mCurrentPackage.packageName); 6960 } 6961 } 6962 if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through"); 6963 } catch (IOException e) { 6964 // We lost our ability to communicate via the pipes. That's worrying 6965 // but potentially recoverable; abandon this package's restore but 6966 // carry on with the next restore target. 6967 Slog.e(TAG, "Unable to route data for restore"); 6968 status = BackupTransport.AGENT_ERROR; 6969 } catch (RemoteException e) { 6970 // The transport went away; terminate the whole operation. Closing 6971 // the sockets will wake up the engine and it will then tidy up the 6972 // remote end. 6973 Slog.e(TAG, "Transport failed during restore"); 6974 status = BackupTransport.TRANSPORT_ERROR; 6975 } finally { 6976 // Close the transport pipes and *our* end of the engine pipe, 6977 // but leave the engine thread's end open so that it properly 6978 // hits EOF and winds up its operations. 6979 IoUtils.closeQuietly(mEnginePipes[1]); 6980 IoUtils.closeQuietly(mTransportPipes[0]); 6981 IoUtils.closeQuietly(mTransportPipes[1]); 6982 6983 // Don't proceed until the engine has torn down the agent etc 6984 eThread.waitForResult(); 6985 6986 // Now we're really done with this one too 6987 IoUtils.closeQuietly(mEnginePipes[0]); 6988 6989 // If we hit a transport-level error, we are done with everything; 6990 // if we hit an agent error we just go back to running the queue. 6991 if (status == BackupTransport.TRANSPORT_OK) { 6992 // Clean finish, so just carry on 6993 nextState = UnifiedRestoreState.RUNNING_QUEUE; 6994 } else { 6995 // Something went wrong somewhere. Whether it was at the transport 6996 // level is immaterial; we need to tell the transport to bail 6997 try { 6998 mTransport.abortFullRestore(); 6999 } catch (RemoteException e) { 7000 // transport itself is dead; make sure we handle this as a 7001 // fatal error 7002 status = BackupTransport.TRANSPORT_ERROR; 7003 } 7004 7005 // We also need to wipe the current target's data, as it's probably 7006 // in an incoherent state. 7007 clearApplicationDataSynchronous(mCurrentPackage.packageName); 7008 7009 // Schedule the next state based on the nature of our failure 7010 if (status == BackupTransport.TRANSPORT_ERROR) { 7011 nextState = UnifiedRestoreState.FINAL; 7012 } else { 7013 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7014 } 7015 } 7016 executeNextState(nextState); 7017 setRunning(false); 7018 } 7019 } 7020 7021 } 7022 7023 class EngineThread implements Runnable { 7024 FullRestoreEngine mEngine; 7025 FileInputStream mEngineStream; 7026 7027 EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) { 7028 mEngine = engine; 7029 engine.setRunning(true); 7030 mEngineStream = new FileInputStream(engineSocket.getFileDescriptor()); 7031 } 7032 7033 public boolean isRunning() { 7034 return mEngine.isRunning(); 7035 } 7036 7037 public int waitForResult() { 7038 return mEngine.waitForResult(); 7039 } 7040 7041 @Override 7042 public void run() { 7043 while (mEngine.isRunning()) { 7044 mEngine.restoreOneFile(mEngineStream); 7045 } 7046 } 7047 } 7048 7049 // state FINAL : tear everything down and we're done. 7050 private void finalizeRestore() { 7051 if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver); 7052 7053 try { 7054 mTransport.finishRestore(); 7055 } catch (Exception e) { 7056 Slog.e(TAG, "Error finishing restore", e); 7057 } 7058 7059 // Tell the observer we're done 7060 if (mObserver != null) { 7061 try { 7062 mObserver.restoreFinished(mStatus); 7063 } catch (RemoteException e) { 7064 Slog.d(TAG, "Restore observer died at restoreFinished"); 7065 } 7066 } 7067 7068 // If we have a PM token, we must under all circumstances be sure to 7069 // handshake when we've finished. 7070 if (mPmToken > 0) { 7071 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken); 7072 try { 7073 mPackageManagerBinder.finishPackageInstall(mPmToken); 7074 } catch (RemoteException e) { /* can't happen */ } 7075 } 7076 7077 // Kick off any work that may be needed regarding app widget restores 7078 AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER); 7079 7080 // If this was a full-system restore, record the ancestral 7081 // dataset information 7082 if (mIsSystemRestore) { 7083 mAncestralPackages = mPmAgent.getRestoredPackages(); 7084 mAncestralToken = mToken; 7085 writeRestoreTokens(); 7086 } 7087 7088 // Furthermore we need to reset the session timeout clock 7089 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 7090 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, 7091 TIMEOUT_RESTORE_INTERVAL); 7092 7093 // done; we can finally release the wakelock and be legitimately done. 7094 Slog.i(TAG, "Restore complete."); 7095 mWakelock.release(); 7096 } 7097 7098 void keyValueAgentErrorCleanup() { 7099 // If the agent fails restore, it might have put the app's data 7100 // into an incoherent state. For consistency we wipe its data 7101 // again in this case before continuing with normal teardown 7102 clearApplicationDataSynchronous(mCurrentPackage.packageName); 7103 keyValueAgentCleanup(); 7104 } 7105 7106 void keyValueAgentCleanup() { 7107 mBackupDataName.delete(); 7108 mStageName.delete(); 7109 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 7110 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 7111 mBackupData = mNewState = null; 7112 7113 // if everything went okay, remember the recorded state now 7114 // 7115 // !!! TODO: the restored data could be migrated on the server 7116 // side into the current dataset. In that case the new state file 7117 // we just created would reflect the data already extant in the 7118 // backend, so there'd be nothing more to do. Until that happens, 7119 // however, we need to make sure that we record the data to the 7120 // current backend dataset. (Yes, this means shipping the data over 7121 // the wire in both directions. That's bad, but consistency comes 7122 // first, then efficiency.) Once we introduce server-side data 7123 // migration to the newly-restored device's dataset, we will change 7124 // the following from a discard of the newly-written state to the 7125 // "correct" operation of renaming into the canonical state blob. 7126 mNewStateName.delete(); // TODO: remove; see above comment 7127 //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this 7128 7129 // If this wasn't the PM pseudopackage, tear down the agent side 7130 if (mCurrentPackage.applicationInfo != null) { 7131 // unbind and tidy up even on timeout or failure 7132 try { 7133 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 7134 7135 // The agent was probably running with a stub Application object, 7136 // which isn't a valid run mode for the main app logic. Shut 7137 // down the app so that next time it's launched, it gets the 7138 // usual full initialization. Note that this is only done for 7139 // full-system restores: when a single app has requested a restore, 7140 // it is explicitly not killed following that operation. 7141 if (mTargetPackage == null && (mCurrentPackage.applicationInfo.flags 7142 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) { 7143 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of " 7144 + mCurrentPackage.applicationInfo.processName); 7145 mActivityManager.killApplicationProcess( 7146 mCurrentPackage.applicationInfo.processName, 7147 mCurrentPackage.applicationInfo.uid); 7148 } 7149 } catch (RemoteException e) { 7150 // can't happen; we run in the same process as the activity manager 7151 } 7152 } 7153 7154 // The caller is responsible for reestablishing the state machine; our 7155 // responsibility here is to clear the decks for whatever comes next. 7156 mBackupHandler.removeMessages(MSG_TIMEOUT, this); 7157 synchronized (mCurrentOpLock) { 7158 mCurrentOperations.clear(); 7159 } 7160 } 7161 7162 @Override 7163 public void operationComplete() { 7164 int size = (int) mBackupDataName.length(); 7165 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size); 7166 7167 // Just go back to running the restore queue 7168 keyValueAgentCleanup(); 7169 7170 // If there was widget state associated with this app, get the OS to 7171 // incorporate it into current bookeeping and then pass that along to 7172 // the app as part of the restore operation. 7173 if (mWidgetData != null) { 7174 restoreWidgetData(mCurrentPackage.packageName, mWidgetData); 7175 } 7176 7177 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7178 } 7179 7180 // A call to agent.doRestore() has timed out 7181 @Override 7182 public void handleTimeout() { 7183 Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName); 7184 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7185 mCurrentPackage.packageName, "restore timeout"); 7186 // Handle like an agent that threw on invocation: wipe it and go on to the next 7187 keyValueAgentErrorCleanup(); 7188 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7189 } 7190 7191 void executeNextState(UnifiedRestoreState nextState) { 7192 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 7193 + this + " nextState=" + nextState); 7194 mState = nextState; 7195 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 7196 mBackupHandler.sendMessage(msg); 7197 } 7198 7199 // restore observer support 7200 void sendStartRestore(int numPackages) { 7201 if (mObserver != null) { 7202 try { 7203 mObserver.restoreStarting(numPackages); 7204 } catch (RemoteException e) { 7205 Slog.w(TAG, "Restore observer went away: startRestore"); 7206 mObserver = null; 7207 } 7208 } 7209 } 7210 7211 void sendOnRestorePackage(String name) { 7212 if (mObserver != null) { 7213 if (mObserver != null) { 7214 try { 7215 mObserver.onUpdate(mCount, name); 7216 } catch (RemoteException e) { 7217 Slog.d(TAG, "Restore observer died in onUpdate"); 7218 mObserver = null; 7219 } 7220 } 7221 } 7222 } 7223 7224 void sendEndRestore() { 7225 if (mObserver != null) { 7226 try { 7227 mObserver.restoreFinished(mStatus); 7228 } catch (RemoteException e) { 7229 Slog.w(TAG, "Restore observer went away: endRestore"); 7230 mObserver = null; 7231 } 7232 } 7233 } 7234 } 7235 7236 class PerformClearTask implements Runnable { 7237 IBackupTransport mTransport; 7238 PackageInfo mPackage; 7239 7240 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) { 7241 mTransport = transport; 7242 mPackage = packageInfo; 7243 } 7244 7245 public void run() { 7246 try { 7247 // Clear the on-device backup state to ensure a full backup next time 7248 File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); 7249 File stateFile = new File(stateDir, mPackage.packageName); 7250 stateFile.delete(); 7251 7252 // Tell the transport to remove all the persistent storage for the app 7253 // TODO - need to handle failures 7254 mTransport.clearBackupData(mPackage); 7255 } catch (RemoteException e) { 7256 // can't happen; the transport is local 7257 } catch (Exception e) { 7258 Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage); 7259 } finally { 7260 try { 7261 // TODO - need to handle failures 7262 mTransport.finishBackup(); 7263 } catch (RemoteException e) { 7264 // can't happen; the transport is local 7265 } 7266 7267 // Last but not least, release the cpu 7268 mWakelock.release(); 7269 } 7270 } 7271 } 7272 7273 class PerformInitializeTask implements Runnable { 7274 HashSet<String> mQueue; 7275 7276 PerformInitializeTask(HashSet<String> transportNames) { 7277 mQueue = transportNames; 7278 } 7279 7280 public void run() { 7281 try { 7282 for (String transportName : mQueue) { 7283 IBackupTransport transport = getTransport(transportName); 7284 if (transport == null) { 7285 Slog.e(TAG, "Requested init for " + transportName + " but not found"); 7286 continue; 7287 } 7288 7289 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); 7290 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName()); 7291 long startRealtime = SystemClock.elapsedRealtime(); 7292 int status = transport.initializeDevice(); 7293 7294 if (status == BackupTransport.TRANSPORT_OK) { 7295 status = transport.finishBackup(); 7296 } 7297 7298 // Okay, the wipe really happened. Clean up our local bookkeeping. 7299 if (status == BackupTransport.TRANSPORT_OK) { 7300 Slog.i(TAG, "Device init successful"); 7301 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); 7302 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 7303 resetBackupState(new File(mBaseStateDir, transport.transportDirName())); 7304 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); 7305 synchronized (mQueueLock) { 7306 recordInitPendingLocked(false, transportName); 7307 } 7308 } else { 7309 // If this didn't work, requeue this one and try again 7310 // after a suitable interval 7311 Slog.e(TAG, "Transport error in initializeDevice()"); 7312 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 7313 synchronized (mQueueLock) { 7314 recordInitPendingLocked(true, transportName); 7315 } 7316 // do this via another alarm to make sure of the wakelock states 7317 long delay = transport.requestBackupTime(); 7318 if (DEBUG) Slog.w(TAG, "init failed on " 7319 + transportName + " resched in " + delay); 7320 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 7321 System.currentTimeMillis() + delay, mRunInitIntent); 7322 } 7323 } 7324 } catch (RemoteException e) { 7325 // can't happen; the transports are local 7326 } catch (Exception e) { 7327 Slog.e(TAG, "Unexpected error performing init", e); 7328 } finally { 7329 // Done; release the wakelock 7330 mWakelock.release(); 7331 } 7332 } 7333 } 7334 7335 private void dataChangedImpl(String packageName) { 7336 HashSet<String> targets = dataChangedTargets(packageName); 7337 dataChangedImpl(packageName, targets); 7338 } 7339 7340 private void dataChangedImpl(String packageName, HashSet<String> targets) { 7341 // Record that we need a backup pass for the caller. Since multiple callers 7342 // may share a uid, we need to note all candidates within that uid and schedule 7343 // a backup pass for each of them. 7344 EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName); 7345 7346 if (targets == null) { 7347 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 7348 + " uid=" + Binder.getCallingUid()); 7349 return; 7350 } 7351 7352 synchronized (mQueueLock) { 7353 // Note that this client has made data changes that need to be backed up 7354 if (targets.contains(packageName)) { 7355 // Add the caller to the set of pending backups. If there is 7356 // one already there, then overwrite it, but no harm done. 7357 BackupRequest req = new BackupRequest(packageName); 7358 if (mPendingBackups.put(packageName, req) == null) { 7359 if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName); 7360 7361 // Journal this request in case of crash. The put() 7362 // operation returned null when this package was not already 7363 // in the set; we want to avoid touching the disk redundantly. 7364 writeToJournalLocked(packageName); 7365 7366 if (MORE_DEBUG) { 7367 int numKeys = mPendingBackups.size(); 7368 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); 7369 for (BackupRequest b : mPendingBackups.values()) { 7370 Slog.d(TAG, " + " + b); 7371 } 7372 } 7373 } 7374 } 7375 } 7376 } 7377 7378 // Note: packageName is currently unused, but may be in the future 7379 private HashSet<String> dataChangedTargets(String packageName) { 7380 // If the caller does not hold the BACKUP permission, it can only request a 7381 // backup of its own data. 7382 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 7383 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 7384 synchronized (mBackupParticipants) { 7385 return mBackupParticipants.get(Binder.getCallingUid()); 7386 } 7387 } 7388 7389 // a caller with full permission can ask to back up any participating app 7390 // !!! TODO: allow backup of ANY app? 7391 HashSet<String> targets = new HashSet<String>(); 7392 synchronized (mBackupParticipants) { 7393 int N = mBackupParticipants.size(); 7394 for (int i = 0; i < N; i++) { 7395 HashSet<String> s = mBackupParticipants.valueAt(i); 7396 if (s != null) { 7397 targets.addAll(s); 7398 } 7399 } 7400 } 7401 return targets; 7402 } 7403 7404 private void writeToJournalLocked(String str) { 7405 RandomAccessFile out = null; 7406 try { 7407 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir); 7408 out = new RandomAccessFile(mJournal, "rws"); 7409 out.seek(out.length()); 7410 out.writeUTF(str); 7411 } catch (IOException e) { 7412 Slog.e(TAG, "Can't write " + str + " to backup journal", e); 7413 mJournal = null; 7414 } finally { 7415 try { if (out != null) out.close(); } catch (IOException e) {} 7416 } 7417 } 7418 7419 // ----- IBackupManager binder interface ----- 7420 7421 public void dataChanged(final String packageName) { 7422 final int callingUserHandle = UserHandle.getCallingUserId(); 7423 if (callingUserHandle != UserHandle.USER_OWNER) { 7424 // App is running under a non-owner user profile. For now, we do not back 7425 // up data from secondary user profiles. 7426 // TODO: backups for all user profiles. 7427 if (MORE_DEBUG) { 7428 Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " 7429 + callingUserHandle); 7430 } 7431 return; 7432 } 7433 7434 final HashSet<String> targets = dataChangedTargets(packageName); 7435 if (targets == null) { 7436 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 7437 + " uid=" + Binder.getCallingUid()); 7438 return; 7439 } 7440 7441 mBackupHandler.post(new Runnable() { 7442 public void run() { 7443 dataChangedImpl(packageName, targets); 7444 } 7445 }); 7446 } 7447 7448 // Clear the given package's backup data from the current transport 7449 public void clearBackupData(String transportName, String packageName) { 7450 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); 7451 PackageInfo info; 7452 try { 7453 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 7454 } catch (NameNotFoundException e) { 7455 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); 7456 return; 7457 } 7458 7459 // If the caller does not hold the BACKUP permission, it can only request a 7460 // wipe of its own backed-up data. 7461 HashSet<String> apps; 7462 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 7463 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 7464 apps = mBackupParticipants.get(Binder.getCallingUid()); 7465 } else { 7466 // a caller with full permission can ask to back up any participating app 7467 // !!! TODO: allow data-clear of ANY app? 7468 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps"); 7469 apps = new HashSet<String>(); 7470 int N = mBackupParticipants.size(); 7471 for (int i = 0; i < N; i++) { 7472 HashSet<String> s = mBackupParticipants.valueAt(i); 7473 if (s != null) { 7474 apps.addAll(s); 7475 } 7476 } 7477 } 7478 7479 // Is the given app an available participant? 7480 if (apps.contains(packageName)) { 7481 // found it; fire off the clear request 7482 if (DEBUG) Slog.v(TAG, "Found the app - running clear process"); 7483 mBackupHandler.removeMessages(MSG_RETRY_CLEAR); 7484 synchronized (mQueueLock) { 7485 final IBackupTransport transport = getTransport(transportName); 7486 if (transport == null) { 7487 // transport is currently unavailable -- make sure to retry 7488 Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR, 7489 new ClearRetryParams(transportName, packageName)); 7490 mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL); 7491 return; 7492 } 7493 long oldId = Binder.clearCallingIdentity(); 7494 mWakelock.acquire(); 7495 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, 7496 new ClearParams(transport, info)); 7497 mBackupHandler.sendMessage(msg); 7498 Binder.restoreCallingIdentity(oldId); 7499 } 7500 } 7501 } 7502 7503 // Run a backup pass immediately for any applications that have declared 7504 // that they have pending updates. 7505 public void backupNow() { 7506 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow"); 7507 7508 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); 7509 synchronized (mQueueLock) { 7510 // Because the alarms we are using can jitter, and we want an *immediate* 7511 // backup pass to happen, we restart the timer beginning with "next time," 7512 // then manually fire the backup trigger intent ourselves. 7513 startBackupAlarmsLocked(BACKUP_INTERVAL); 7514 try { 7515 mRunBackupIntent.send(); 7516 } catch (PendingIntent.CanceledException e) { 7517 // should never happen 7518 Slog.e(TAG, "run-backup intent cancelled!"); 7519 } 7520 } 7521 } 7522 7523 boolean deviceIsProvisioned() { 7524 final ContentResolver resolver = mContext.getContentResolver(); 7525 return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); 7526 } 7527 7528 // Run a *full* backup pass for the given packages, writing the resulting data stream 7529 // to the supplied file descriptor. This method is synchronous and does not return 7530 // to the caller until the backup has been completed. 7531 // 7532 // This is the variant used by 'adb backup'; it requires on-screen confirmation 7533 // by the user because it can be used to offload data over untrusted USB. 7534 @Override 7535 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, 7536 boolean includeObbs, boolean includeShared, boolean doWidgets, 7537 boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) { 7538 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); 7539 7540 final int callingUserHandle = UserHandle.getCallingUserId(); 7541 if (callingUserHandle != UserHandle.USER_OWNER) { 7542 throw new IllegalStateException("Backup supported only for the device owner"); 7543 } 7544 7545 // Validate 7546 if (!doAllApps) { 7547 if (!includeShared) { 7548 // If we're backing up shared data (sdcard or equivalent), then we can run 7549 // without any supplied app names. Otherwise, we'd be doing no work, so 7550 // report the error. 7551 if (pkgList == null || pkgList.length == 0) { 7552 throw new IllegalArgumentException( 7553 "Backup requested but neither shared nor any apps named"); 7554 } 7555 } 7556 } 7557 7558 long oldId = Binder.clearCallingIdentity(); 7559 try { 7560 // Doesn't make sense to do a full backup prior to setup 7561 if (!deviceIsProvisioned()) { 7562 Slog.i(TAG, "Full backup not supported before setup"); 7563 return; 7564 } 7565 7566 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks 7567 + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps 7568 + " pkgs=" + pkgList); 7569 Slog.i(TAG, "Beginning full backup..."); 7570 7571 FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs, 7572 includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList); 7573 final int token = generateToken(); 7574 synchronized (mFullConfirmations) { 7575 mFullConfirmations.put(token, params); 7576 } 7577 7578 // start up the confirmation UI 7579 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token); 7580 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { 7581 Slog.e(TAG, "Unable to launch full backup confirmation"); 7582 mFullConfirmations.delete(token); 7583 return; 7584 } 7585 7586 // make sure the screen is lit for the user interaction 7587 mPowerManager.userActivity(SystemClock.uptimeMillis(), false); 7588 7589 // start the confirmation countdown 7590 startConfirmationTimeout(token, params); 7591 7592 // wait for the backup to be performed 7593 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion..."); 7594 waitForCompletion(params); 7595 } finally { 7596 try { 7597 fd.close(); 7598 } catch (IOException e) { 7599 // just eat it 7600 } 7601 Binder.restoreCallingIdentity(oldId); 7602 Slog.d(TAG, "Full backup processing complete."); 7603 } 7604 } 7605 7606 @Override 7607 public void fullTransportBackup(String[] pkgNames) { 7608 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, 7609 "fullTransportBackup"); 7610 7611 final int callingUserHandle = UserHandle.getCallingUserId(); 7612 if (callingUserHandle != UserHandle.USER_OWNER) { 7613 throw new IllegalStateException("Restore supported only for the device owner"); 7614 } 7615 7616 if (DEBUG) { 7617 Slog.d(TAG, "fullTransportBackup()"); 7618 } 7619 7620 AtomicBoolean latch = new AtomicBoolean(false); 7621 PerformFullTransportBackupTask task = new PerformFullTransportBackupTask(null, pkgNames, latch); 7622 (new Thread(task, "full-transport-master")).start(); 7623 synchronized (latch) { 7624 try { 7625 while (latch.get() == false) { 7626 latch.wait(); 7627 } 7628 } catch (InterruptedException e) {} 7629 } 7630 if (DEBUG) { 7631 Slog.d(TAG, "Done with full transport backup."); 7632 } 7633 } 7634 7635 @Override 7636 public void fullRestore(ParcelFileDescriptor fd) { 7637 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); 7638 7639 final int callingUserHandle = UserHandle.getCallingUserId(); 7640 if (callingUserHandle != UserHandle.USER_OWNER) { 7641 throw new IllegalStateException("Restore supported only for the device owner"); 7642 } 7643 7644 long oldId = Binder.clearCallingIdentity(); 7645 7646 try { 7647 // Check whether the device has been provisioned -- we don't handle 7648 // full restores prior to completing the setup process. 7649 if (!deviceIsProvisioned()) { 7650 Slog.i(TAG, "Full restore not permitted before setup"); 7651 return; 7652 } 7653 7654 Slog.i(TAG, "Beginning full restore..."); 7655 7656 FullRestoreParams params = new FullRestoreParams(fd); 7657 final int token = generateToken(); 7658 synchronized (mFullConfirmations) { 7659 mFullConfirmations.put(token, params); 7660 } 7661 7662 // start up the confirmation UI 7663 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token); 7664 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) { 7665 Slog.e(TAG, "Unable to launch full restore confirmation"); 7666 mFullConfirmations.delete(token); 7667 return; 7668 } 7669 7670 // make sure the screen is lit for the user interaction 7671 mPowerManager.userActivity(SystemClock.uptimeMillis(), false); 7672 7673 // start the confirmation countdown 7674 startConfirmationTimeout(token, params); 7675 7676 // wait for the restore to be performed 7677 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion..."); 7678 waitForCompletion(params); 7679 } finally { 7680 try { 7681 fd.close(); 7682 } catch (IOException e) { 7683 Slog.w(TAG, "Error trying to close fd after full restore: " + e); 7684 } 7685 Binder.restoreCallingIdentity(oldId); 7686 Slog.i(TAG, "Full restore processing complete."); 7687 } 7688 } 7689 7690 boolean startConfirmationUi(int token, String action) { 7691 try { 7692 Intent confIntent = new Intent(action); 7693 confIntent.setClassName("com.android.backupconfirm", 7694 "com.android.backupconfirm.BackupRestoreConfirmation"); 7695 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token); 7696 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 7697 mContext.startActivity(confIntent); 7698 } catch (ActivityNotFoundException e) { 7699 return false; 7700 } 7701 return true; 7702 } 7703 7704 void startConfirmationTimeout(int token, FullParams params) { 7705 if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after " 7706 + TIMEOUT_FULL_CONFIRMATION + " millis"); 7707 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, 7708 token, 0, params); 7709 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION); 7710 } 7711 7712 void waitForCompletion(FullParams params) { 7713 synchronized (params.latch) { 7714 while (params.latch.get() == false) { 7715 try { 7716 params.latch.wait(); 7717 } catch (InterruptedException e) { /* never interrupted */ } 7718 } 7719 } 7720 } 7721 7722 void signalFullBackupRestoreCompletion(FullParams params) { 7723 synchronized (params.latch) { 7724 params.latch.set(true); 7725 params.latch.notifyAll(); 7726 } 7727 } 7728 7729 // Confirm that the previously-requested full backup/restore operation can proceed. This 7730 // is used to require a user-facing disclosure about the operation. 7731 @Override 7732 public void acknowledgeFullBackupOrRestore(int token, boolean allow, 7733 String curPassword, String encPpassword, IFullBackupRestoreObserver observer) { 7734 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token 7735 + " allow=" + allow); 7736 7737 // TODO: possibly require not just this signature-only permission, but even 7738 // require that the specific designated confirmation-UI app uid is the caller? 7739 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore"); 7740 7741 long oldId = Binder.clearCallingIdentity(); 7742 try { 7743 7744 FullParams params; 7745 synchronized (mFullConfirmations) { 7746 params = mFullConfirmations.get(token); 7747 if (params != null) { 7748 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params); 7749 mFullConfirmations.delete(token); 7750 7751 if (allow) { 7752 final int verb = params instanceof FullBackupParams 7753 ? MSG_RUN_ADB_BACKUP 7754 : MSG_RUN_ADB_RESTORE; 7755 7756 params.observer = observer; 7757 params.curPassword = curPassword; 7758 7759 boolean isEncrypted; 7760 try { 7761 isEncrypted = (mMountService.getEncryptionState() != 7762 IMountService.ENCRYPTION_STATE_NONE); 7763 if (isEncrypted) Slog.w(TAG, "Device is encrypted; forcing enc password"); 7764 } catch (RemoteException e) { 7765 // couldn't contact the mount service; fail "safe" and assume encryption 7766 Slog.e(TAG, "Unable to contact mount service!"); 7767 isEncrypted = true; 7768 } 7769 params.encryptPassword = (isEncrypted) ? curPassword : encPpassword; 7770 7771 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb); 7772 mWakelock.acquire(); 7773 Message msg = mBackupHandler.obtainMessage(verb, params); 7774 mBackupHandler.sendMessage(msg); 7775 } else { 7776 Slog.w(TAG, "User rejected full backup/restore operation"); 7777 // indicate completion without having actually transferred any data 7778 signalFullBackupRestoreCompletion(params); 7779 } 7780 } else { 7781 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token"); 7782 } 7783 } 7784 } finally { 7785 Binder.restoreCallingIdentity(oldId); 7786 } 7787 } 7788 7789 // Enable/disable the backup service 7790 @Override 7791 public void setBackupEnabled(boolean enable) { 7792 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 7793 "setBackupEnabled"); 7794 7795 Slog.i(TAG, "Backup enabled => " + enable); 7796 7797 long oldId = Binder.clearCallingIdentity(); 7798 try { 7799 boolean wasEnabled = mEnabled; 7800 synchronized (this) { 7801 Settings.Secure.putInt(mContext.getContentResolver(), 7802 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0); 7803 mEnabled = enable; 7804 } 7805 7806 synchronized (mQueueLock) { 7807 if (enable && !wasEnabled && mProvisioned) { 7808 // if we've just been enabled, start scheduling backup passes 7809 startBackupAlarmsLocked(BACKUP_INTERVAL); 7810 } else if (!enable) { 7811 // No longer enabled, so stop running backups 7812 if (DEBUG) Slog.i(TAG, "Opting out of backup"); 7813 7814 mAlarmManager.cancel(mRunBackupIntent); 7815 7816 // This also constitutes an opt-out, so we wipe any data for 7817 // this device from the backend. We start that process with 7818 // an alarm in order to guarantee wakelock states. 7819 if (wasEnabled && mProvisioned) { 7820 // NOTE: we currently flush every registered transport, not just 7821 // the currently-active one. 7822 HashSet<String> allTransports; 7823 synchronized (mTransports) { 7824 allTransports = new HashSet<String>(mTransports.keySet()); 7825 } 7826 // build the set of transports for which we are posting an init 7827 for (String transport : allTransports) { 7828 recordInitPendingLocked(true, transport); 7829 } 7830 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 7831 mRunInitIntent); 7832 } 7833 } 7834 } 7835 } finally { 7836 Binder.restoreCallingIdentity(oldId); 7837 } 7838 } 7839 7840 // Enable/disable automatic restore of app data at install time 7841 public void setAutoRestore(boolean doAutoRestore) { 7842 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 7843 "setAutoRestore"); 7844 7845 Slog.i(TAG, "Auto restore => " + doAutoRestore); 7846 7847 synchronized (this) { 7848 Settings.Secure.putInt(mContext.getContentResolver(), 7849 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0); 7850 mAutoRestore = doAutoRestore; 7851 } 7852 } 7853 7854 // Mark the backup service as having been provisioned 7855 public void setBackupProvisioned(boolean available) { 7856 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 7857 "setBackupProvisioned"); 7858 /* 7859 * This is now a no-op; provisioning is simply the device's own setup state. 7860 */ 7861 } 7862 7863 private void startBackupAlarmsLocked(long delayBeforeFirstBackup) { 7864 // We used to use setInexactRepeating(), but that may be linked to 7865 // backups running at :00 more often than not, creating load spikes. 7866 // Schedule at an exact time for now, and also add a bit of "fuzz". 7867 7868 Random random = new Random(); 7869 long when = System.currentTimeMillis() + delayBeforeFirstBackup + 7870 random.nextInt(FUZZ_MILLIS); 7871 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when, 7872 BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent); 7873 mNextBackupPass = when; 7874 } 7875 7876 // Report whether the backup mechanism is currently enabled 7877 public boolean isBackupEnabled() { 7878 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); 7879 return mEnabled; // no need to synchronize just to read it 7880 } 7881 7882 // Report the name of the currently active transport 7883 public String getCurrentTransport() { 7884 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 7885 "getCurrentTransport"); 7886 if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); 7887 return mCurrentTransport; 7888 } 7889 7890 // Report all known, available backup transports 7891 public String[] listAllTransports() { 7892 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); 7893 7894 String[] list = null; 7895 ArrayList<String> known = new ArrayList<String>(); 7896 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { 7897 if (entry.getValue() != null) { 7898 known.add(entry.getKey()); 7899 } 7900 } 7901 7902 if (known.size() > 0) { 7903 list = new String[known.size()]; 7904 known.toArray(list); 7905 } 7906 return list; 7907 } 7908 7909 // Select which transport to use for the next backup operation. If the given 7910 // name is not one of the available transports, no action is taken and the method 7911 // returns null. 7912 public String selectBackupTransport(String transport) { 7913 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport"); 7914 7915 synchronized (mTransports) { 7916 String prevTransport = null; 7917 if (mTransports.get(transport) != null) { 7918 prevTransport = mCurrentTransport; 7919 mCurrentTransport = transport; 7920 Settings.Secure.putString(mContext.getContentResolver(), 7921 Settings.Secure.BACKUP_TRANSPORT, transport); 7922 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport 7923 + " returning " + prevTransport); 7924 } else { 7925 Slog.w(TAG, "Attempt to select unavailable transport " + transport); 7926 } 7927 return prevTransport; 7928 } 7929 } 7930 7931 // Supply the configuration Intent for the given transport. If the name is not one 7932 // of the available transports, or if the transport does not supply any configuration 7933 // UI, the method returns null. 7934 public Intent getConfigurationIntent(String transportName) { 7935 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 7936 "getConfigurationIntent"); 7937 7938 synchronized (mTransports) { 7939 final IBackupTransport transport = mTransports.get(transportName); 7940 if (transport != null) { 7941 try { 7942 final Intent intent = transport.configurationIntent(); 7943 if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent " 7944 + intent); 7945 return intent; 7946 } catch (RemoteException e) { 7947 /* fall through to return null */ 7948 } 7949 } 7950 } 7951 7952 return null; 7953 } 7954 7955 // Supply the configuration summary string for the given transport. If the name is 7956 // not one of the available transports, or if the transport does not supply any 7957 // summary / destination string, the method can return null. 7958 // 7959 // This string is used VERBATIM as the summary text of the relevant Settings item! 7960 public String getDestinationString(String transportName) { 7961 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 7962 "getDestinationString"); 7963 7964 synchronized (mTransports) { 7965 final IBackupTransport transport = mTransports.get(transportName); 7966 if (transport != null) { 7967 try { 7968 final String text = transport.currentDestinationString(); 7969 if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text); 7970 return text; 7971 } catch (RemoteException e) { 7972 /* fall through to return null */ 7973 } 7974 } 7975 } 7976 7977 return null; 7978 } 7979 7980 // Callback: a requested backup agent has been instantiated. This should only 7981 // be called from the Activity Manager. 7982 public void agentConnected(String packageName, IBinder agentBinder) { 7983 synchronized(mAgentConnectLock) { 7984 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 7985 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); 7986 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); 7987 mConnectedAgent = agent; 7988 mConnecting = false; 7989 } else { 7990 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 7991 + " claiming agent connected"); 7992 } 7993 mAgentConnectLock.notifyAll(); 7994 } 7995 } 7996 7997 // Callback: a backup agent has failed to come up, or has unexpectedly quit. 7998 // If the agent failed to come up in the first place, the agentBinder argument 7999 // will be null. This should only be called from the Activity Manager. 8000 public void agentDisconnected(String packageName) { 8001 // TODO: handle backup being interrupted 8002 synchronized(mAgentConnectLock) { 8003 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 8004 mConnectedAgent = null; 8005 mConnecting = false; 8006 } else { 8007 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 8008 + " claiming agent disconnected"); 8009 } 8010 mAgentConnectLock.notifyAll(); 8011 } 8012 } 8013 8014 // An application being installed will need a restore pass, then the Package Manager 8015 // will need to be told when the restore is finished. 8016 public void restoreAtInstall(String packageName, int token) { 8017 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 8018 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 8019 + " attemping install-time restore"); 8020 return; 8021 } 8022 8023 boolean skip = false; 8024 8025 long restoreSet = getAvailableRestoreToken(packageName); 8026 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName 8027 + " token=" + Integer.toHexString(token) 8028 + " restoreSet=" + Long.toHexString(restoreSet)); 8029 if (restoreSet == 0) { 8030 if (MORE_DEBUG) Slog.i(TAG, "No restore set"); 8031 skip = true; 8032 } 8033 8034 // Do we have a transport to fetch data for us? 8035 IBackupTransport transport = getTransport(mCurrentTransport); 8036 if (transport == null) { 8037 if (DEBUG) Slog.w(TAG, "No transport"); 8038 skip = true; 8039 } 8040 8041 if (!skip && mAutoRestore && mProvisioned) { 8042 try { 8043 // okay, we're going to attempt a restore of this package from this restore set. 8044 // The eventual message back into the Package Manager to run the post-install 8045 // steps for 'token' will be issued from the restore handling code. 8046 8047 // This can throw and so *must* happen before the wakelock is acquired 8048 String dirName = transport.transportDirName(); 8049 8050 // We can use a synthetic PackageInfo here because: 8051 // 1. We know it's valid, since the Package Manager supplied the name 8052 // 2. Only the packageName field will be used by the restore code 8053 PackageInfo pkg = new PackageInfo(); 8054 pkg.packageName = packageName; 8055 8056 mWakelock.acquire(); 8057 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 8058 msg.obj = new RestoreParams(transport, dirName, null, 8059 restoreSet, pkg, token); 8060 mBackupHandler.sendMessage(msg); 8061 } catch (RemoteException e) { 8062 // Binding to the transport broke; back off and proceed with the installation. 8063 Slog.e(TAG, "Unable to contact transport"); 8064 skip = true; 8065 } 8066 } 8067 8068 if (skip) { 8069 // Auto-restore disabled or no way to attempt a restore; just tell the Package 8070 // Manager to proceed with the post-install handling for this package. 8071 if (DEBUG) Slog.v(TAG, "Skipping"); 8072 try { 8073 mPackageManagerBinder.finishPackageInstall(token); 8074 } catch (RemoteException e) { /* can't happen */ } 8075 } 8076 } 8077 8078 // Hand off a restore session 8079 public IRestoreSession beginRestoreSession(String packageName, String transport) { 8080 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName 8081 + " transport=" + transport); 8082 8083 boolean needPermission = true; 8084 if (transport == null) { 8085 transport = mCurrentTransport; 8086 8087 if (packageName != null) { 8088 PackageInfo app = null; 8089 try { 8090 app = mPackageManager.getPackageInfo(packageName, 0); 8091 } catch (NameNotFoundException nnf) { 8092 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 8093 throw new IllegalArgumentException("Package " + packageName + " not found"); 8094 } 8095 8096 if (app.applicationInfo.uid == Binder.getCallingUid()) { 8097 // So: using the current active transport, and the caller has asked 8098 // that its own package will be restored. In this narrow use case 8099 // we do not require the caller to hold the permission. 8100 needPermission = false; 8101 } 8102 } 8103 } 8104 8105 if (needPermission) { 8106 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8107 "beginRestoreSession"); 8108 } else { 8109 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed"); 8110 } 8111 8112 synchronized(this) { 8113 if (mActiveRestoreSession != null) { 8114 Slog.d(TAG, "Restore session requested but one already active"); 8115 return null; 8116 } 8117 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport); 8118 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 8119 } 8120 return mActiveRestoreSession; 8121 } 8122 8123 void clearRestoreSession(ActiveRestoreSession currentSession) { 8124 synchronized(this) { 8125 if (currentSession != mActiveRestoreSession) { 8126 Slog.e(TAG, "ending non-current restore session"); 8127 } else { 8128 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout"); 8129 mActiveRestoreSession = null; 8130 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 8131 } 8132 } 8133 } 8134 8135 // Note that a currently-active backup agent has notified us that it has 8136 // completed the given outstanding asynchronous backup/restore operation. 8137 @Override 8138 public void opComplete(int token) { 8139 if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token)); 8140 Operation op = null; 8141 synchronized (mCurrentOpLock) { 8142 op = mCurrentOperations.get(token); 8143 if (op != null) { 8144 op.state = OP_ACKNOWLEDGED; 8145 } 8146 mCurrentOpLock.notifyAll(); 8147 } 8148 8149 // The completion callback, if any, is invoked on the handler 8150 if (op != null && op.callback != null) { 8151 Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback); 8152 mBackupHandler.sendMessage(msg); 8153 } 8154 } 8155 8156 // ----- Restore session ----- 8157 8158 class ActiveRestoreSession extends IRestoreSession.Stub { 8159 private static final String TAG = "RestoreSession"; 8160 8161 private String mPackageName; 8162 private IBackupTransport mRestoreTransport = null; 8163 RestoreSet[] mRestoreSets = null; 8164 boolean mEnded = false; 8165 8166 ActiveRestoreSession(String packageName, String transport) { 8167 mPackageName = packageName; 8168 mRestoreTransport = getTransport(transport); 8169 } 8170 8171 // --- Binder interface --- 8172 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) { 8173 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8174 "getAvailableRestoreSets"); 8175 if (observer == null) { 8176 throw new IllegalArgumentException("Observer must not be null"); 8177 } 8178 8179 if (mEnded) { 8180 throw new IllegalStateException("Restore session already ended"); 8181 } 8182 8183 long oldId = Binder.clearCallingIdentity(); 8184 try { 8185 if (mRestoreTransport == null) { 8186 Slog.w(TAG, "Null transport getting restore sets"); 8187 return -1; 8188 } 8189 // spin off the transport request to our service thread 8190 mWakelock.acquire(); 8191 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS, 8192 new RestoreGetSetsParams(mRestoreTransport, this, observer)); 8193 mBackupHandler.sendMessage(msg); 8194 return 0; 8195 } catch (Exception e) { 8196 Slog.e(TAG, "Error in getAvailableRestoreSets", e); 8197 return -1; 8198 } finally { 8199 Binder.restoreCallingIdentity(oldId); 8200 } 8201 } 8202 8203 public synchronized int restoreAll(long token, IRestoreObserver observer) { 8204 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8205 "performRestore"); 8206 8207 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token) 8208 + " observer=" + observer); 8209 8210 if (mEnded) { 8211 throw new IllegalStateException("Restore session already ended"); 8212 } 8213 8214 if (mRestoreTransport == null || mRestoreSets == null) { 8215 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 8216 return -1; 8217 } 8218 8219 if (mPackageName != null) { 8220 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 8221 return -1; 8222 } 8223 8224 String dirName; 8225 try { 8226 dirName = mRestoreTransport.transportDirName(); 8227 } catch (RemoteException e) { 8228 // Transport went AWOL; fail. 8229 Slog.e(TAG, "Unable to contact transport for restore"); 8230 return -1; 8231 } 8232 8233 synchronized (mQueueLock) { 8234 for (int i = 0; i < mRestoreSets.length; i++) { 8235 if (token == mRestoreSets[i].token) { 8236 long oldId = Binder.clearCallingIdentity(); 8237 mWakelock.acquire(); 8238 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 8239 msg.obj = new RestoreParams(mRestoreTransport, dirName, 8240 observer, token); 8241 mBackupHandler.sendMessage(msg); 8242 Binder.restoreCallingIdentity(oldId); 8243 return 0; 8244 } 8245 } 8246 } 8247 8248 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 8249 return -1; 8250 } 8251 8252 // Restores of more than a single package are treated as 'system' restores 8253 public synchronized int restoreSome(long token, IRestoreObserver observer, 8254 String[] packages) { 8255 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 8256 "performRestore"); 8257 8258 if (DEBUG) { 8259 StringBuilder b = new StringBuilder(128); 8260 b.append("restoreSome token="); 8261 b.append(Long.toHexString(token)); 8262 b.append(" observer="); 8263 b.append(observer.toString()); 8264 b.append(" packages="); 8265 if (packages == null) { 8266 b.append("null"); 8267 } else { 8268 b.append('{'); 8269 boolean first = true; 8270 for (String s : packages) { 8271 if (!first) { 8272 b.append(", "); 8273 } else first = false; 8274 b.append(s); 8275 } 8276 b.append('}'); 8277 } 8278 Slog.d(TAG, b.toString()); 8279 } 8280 8281 if (mEnded) { 8282 throw new IllegalStateException("Restore session already ended"); 8283 } 8284 8285 if (mRestoreTransport == null || mRestoreSets == null) { 8286 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 8287 return -1; 8288 } 8289 8290 if (mPackageName != null) { 8291 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 8292 return -1; 8293 } 8294 8295 String dirName; 8296 try { 8297 dirName = mRestoreTransport.transportDirName(); 8298 } catch (RemoteException e) { 8299 // Transport went AWOL; fail. 8300 Slog.e(TAG, "Unable to contact transport for restore"); 8301 return -1; 8302 } 8303 8304 synchronized (mQueueLock) { 8305 for (int i = 0; i < mRestoreSets.length; i++) { 8306 if (token == mRestoreSets[i].token) { 8307 long oldId = Binder.clearCallingIdentity(); 8308 mWakelock.acquire(); 8309 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 8310 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, 8311 packages, packages.length > 1); 8312 mBackupHandler.sendMessage(msg); 8313 Binder.restoreCallingIdentity(oldId); 8314 return 0; 8315 } 8316 } 8317 } 8318 8319 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 8320 return -1; 8321 } 8322 8323 public synchronized int restorePackage(String packageName, IRestoreObserver observer) { 8324 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); 8325 8326 if (mEnded) { 8327 throw new IllegalStateException("Restore session already ended"); 8328 } 8329 8330 if (mPackageName != null) { 8331 if (! mPackageName.equals(packageName)) { 8332 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName 8333 + " on session for package " + mPackageName); 8334 return -1; 8335 } 8336 } 8337 8338 PackageInfo app = null; 8339 try { 8340 app = mPackageManager.getPackageInfo(packageName, 0); 8341 } catch (NameNotFoundException nnf) { 8342 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 8343 return -1; 8344 } 8345 8346 // If the caller is not privileged and is not coming from the target 8347 // app's uid, throw a permission exception back to the caller. 8348 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP, 8349 Binder.getCallingPid(), Binder.getCallingUid()); 8350 if ((perm == PackageManager.PERMISSION_DENIED) && 8351 (app.applicationInfo.uid != Binder.getCallingUid())) { 8352 Slog.w(TAG, "restorePackage: bad packageName=" + packageName 8353 + " or calling uid=" + Binder.getCallingUid()); 8354 throw new SecurityException("No permission to restore other packages"); 8355 } 8356 8357 // If the package has no backup agent, we obviously cannot proceed 8358 if (app.applicationInfo.backupAgentName == null) { 8359 Slog.w(TAG, "Asked to restore package " + packageName + " with no agent"); 8360 return -1; 8361 } 8362 8363 // So far so good; we're allowed to try to restore this package. Now 8364 // check whether there is data for it in the current dataset, falling back 8365 // to the ancestral dataset if not. 8366 long token = getAvailableRestoreToken(packageName); 8367 8368 // If we didn't come up with a place to look -- no ancestral dataset and 8369 // the app has never been backed up from this device -- there's nothing 8370 // to do but return failure. 8371 if (token == 0) { 8372 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring"); 8373 return -1; 8374 } 8375 8376 String dirName; 8377 try { 8378 dirName = mRestoreTransport.transportDirName(); 8379 } catch (RemoteException e) { 8380 // Transport went AWOL; fail. 8381 Slog.e(TAG, "Unable to contact transport for restore"); 8382 return -1; 8383 } 8384 8385 // Ready to go: enqueue the restore request and claim success 8386 long oldId = Binder.clearCallingIdentity(); 8387 mWakelock.acquire(); 8388 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 8389 msg.obj = new RestoreParams(mRestoreTransport, dirName, 8390 observer, token, app, 0); 8391 mBackupHandler.sendMessage(msg); 8392 Binder.restoreCallingIdentity(oldId); 8393 return 0; 8394 } 8395 8396 // Posted to the handler to tear down a restore session in a cleanly synchronized way 8397 class EndRestoreRunnable implements Runnable { 8398 BackupManagerService mBackupManager; 8399 ActiveRestoreSession mSession; 8400 8401 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) { 8402 mBackupManager = manager; 8403 mSession = session; 8404 } 8405 8406 public void run() { 8407 // clean up the session's bookkeeping 8408 synchronized (mSession) { 8409 try { 8410 if (mSession.mRestoreTransport != null) { 8411 mSession.mRestoreTransport.finishRestore(); 8412 } 8413 } catch (Exception e) { 8414 Slog.e(TAG, "Error in finishRestore", e); 8415 } finally { 8416 mSession.mRestoreTransport = null; 8417 mSession.mEnded = true; 8418 } 8419 } 8420 8421 // clean up the BackupManagerImpl side of the bookkeeping 8422 // and cancel any pending timeout message 8423 mBackupManager.clearRestoreSession(mSession); 8424 } 8425 } 8426 8427 public synchronized void endRestoreSession() { 8428 if (DEBUG) Slog.d(TAG, "endRestoreSession"); 8429 8430 if (mEnded) { 8431 throw new IllegalStateException("Restore session already ended"); 8432 } 8433 8434 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this)); 8435 } 8436 } 8437 8438 @Override 8439 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 8440 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 8441 8442 long identityToken = Binder.clearCallingIdentity(); 8443 try { 8444 if (args != null) { 8445 for (String arg : args) { 8446 if ("-h".equals(arg)) { 8447 pw.println("'dumpsys backup' optional arguments:"); 8448 pw.println(" -h : this help text"); 8449 pw.println(" a[gents] : dump information about defined backup agents"); 8450 return; 8451 } else if ("agents".startsWith(arg)) { 8452 dumpAgents(pw); 8453 return; 8454 } 8455 } 8456 } 8457 dumpInternal(pw); 8458 } finally { 8459 Binder.restoreCallingIdentity(identityToken); 8460 } 8461 } 8462 8463 private void dumpAgents(PrintWriter pw) { 8464 List<PackageInfo> agentPackages = allAgentPackages(); 8465 pw.println("Defined backup agents:"); 8466 for (PackageInfo pkg : agentPackages) { 8467 pw.print(" "); 8468 pw.print(pkg.packageName); pw.println(':'); 8469 pw.print(" "); pw.println(pkg.applicationInfo.backupAgentName); 8470 } 8471 } 8472 8473 private void dumpInternal(PrintWriter pw) { 8474 synchronized (mQueueLock) { 8475 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") 8476 + " / " + (!mProvisioned ? "not " : "") + "provisioned / " 8477 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); 8478 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); 8479 if (mBackupRunning) pw.println("Backup currently running"); 8480 pw.println("Last backup pass started: " + mLastBackupPass 8481 + " (now = " + System.currentTimeMillis() + ')'); 8482 pw.println(" next scheduled: " + mNextBackupPass); 8483 8484 pw.println("Available transports:"); 8485 for (String t : listAllTransports()) { 8486 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t); 8487 try { 8488 IBackupTransport transport = getTransport(t); 8489 File dir = new File(mBaseStateDir, transport.transportDirName()); 8490 pw.println(" destination: " + transport.currentDestinationString()); 8491 pw.println(" intent: " + transport.configurationIntent()); 8492 for (File f : dir.listFiles()) { 8493 pw.println(" " + f.getName() + " - " + f.length() + " state bytes"); 8494 } 8495 } catch (Exception e) { 8496 Slog.e(TAG, "Error in transport", e); 8497 pw.println(" Error: " + e); 8498 } 8499 } 8500 8501 pw.println("Pending init: " + mPendingInits.size()); 8502 for (String s : mPendingInits) { 8503 pw.println(" " + s); 8504 } 8505 8506 if (DEBUG_BACKUP_TRACE) { 8507 synchronized (mBackupTrace) { 8508 if (!mBackupTrace.isEmpty()) { 8509 pw.println("Most recent backup trace:"); 8510 for (String s : mBackupTrace) { 8511 pw.println(" " + s); 8512 } 8513 } 8514 } 8515 } 8516 8517 int N = mBackupParticipants.size(); 8518 pw.println("Participants:"); 8519 for (int i=0; i<N; i++) { 8520 int uid = mBackupParticipants.keyAt(i); 8521 pw.print(" uid: "); 8522 pw.println(uid); 8523 HashSet<String> participants = mBackupParticipants.valueAt(i); 8524 for (String app: participants) { 8525 pw.println(" " + app); 8526 } 8527 } 8528 8529 pw.println("Ancestral packages: " 8530 + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); 8531 if (mAncestralPackages != null) { 8532 for (String pkg : mAncestralPackages) { 8533 pw.println(" " + pkg); 8534 } 8535 } 8536 8537 pw.println("Ever backed up: " + mEverStoredApps.size()); 8538 for (String pkg : mEverStoredApps) { 8539 pw.println(" " + pkg); 8540 } 8541 8542 pw.println("Pending backup: " + mPendingBackups.size()); 8543 for (BackupRequest req : mPendingBackups.values()) { 8544 pw.println(" " + req); 8545 } 8546 } 8547 } 8548} 8549