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