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