BackupManagerService.java revision 339b53a8e678f18da906906f5f32ed57772102bb
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 try { 3172 int size = (int) mBackupDataName.length(); 3173 if (size > 0) { 3174 if (mStatus == BackupTransport.TRANSPORT_OK) { 3175 backupData = ParcelFileDescriptor.open(mBackupDataName, 3176 ParcelFileDescriptor.MODE_READ_ONLY); 3177 addBackupTrace("sending data to transport"); 3178 int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0; 3179 mStatus = mTransport.performBackup(mCurrentPackage, backupData, flags); 3180 } 3181 3182 // TODO - We call finishBackup() for each application backed up, because 3183 // we need to know now whether it succeeded or failed. Instead, we should 3184 // hold off on finishBackup() until the end, which implies holding off on 3185 // renaming *all* the output state files (see below) until that happens. 3186 3187 addBackupTrace("data delivered: " + mStatus); 3188 if (mStatus == BackupTransport.TRANSPORT_OK) { 3189 addBackupTrace("finishing op on transport"); 3190 mStatus = mTransport.finishBackup(); 3191 addBackupTrace("finished: " + mStatus); 3192 } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 3193 addBackupTrace("transport rejected package"); 3194 } 3195 } else { 3196 if (MORE_DEBUG) Slog.i(TAG, "no backup data written; not calling transport"); 3197 addBackupTrace("no data to send"); 3198 } 3199 3200 if (mStatus == BackupTransport.TRANSPORT_OK) { 3201 // After successful transport, delete the now-stale data 3202 // and juggle the files so that next time we supply the agent 3203 // with the new state file it just created. 3204 mBackupDataName.delete(); 3205 mNewStateName.renameTo(mSavedStateName); 3206 sendBackupOnResult(mObserver, pkgName, BackupManager.SUCCESS); 3207 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size); 3208 logBackupComplete(pkgName); 3209 } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 3210 // The transport has rejected backup of this specific package. Roll it 3211 // back but proceed with running the rest of the queue. 3212 mBackupDataName.delete(); 3213 mNewStateName.delete(); 3214 sendBackupOnResult(mObserver, pkgName, 3215 BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED); 3216 EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected"); 3217 } else { 3218 // Actual transport-level failure to communicate the data to the backend 3219 sendBackupOnResult(mObserver, pkgName, BackupManager.ERROR_TRANSPORT_ABORTED); 3220 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); 3221 } 3222 } catch (Exception e) { 3223 sendBackupOnResult(mObserver, pkgName, BackupManager.ERROR_TRANSPORT_ABORTED); 3224 Slog.e(TAG, "Transport error backing up " + pkgName, e); 3225 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); 3226 mStatus = BackupTransport.TRANSPORT_ERROR; 3227 } finally { 3228 try { if (backupData != null) backupData.close(); } catch (IOException e) {} 3229 } 3230 3231 final BackupState nextState; 3232 if (mStatus == BackupTransport.TRANSPORT_OK 3233 || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 3234 // Success or single-package rejection. Proceed with the next app if any, 3235 // otherwise we're done. 3236 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE; 3237 } else { 3238 // Any other error here indicates a transport-level failure. That means 3239 // we need to halt everything and reschedule everything for next time. 3240 revertAndEndBackup(); 3241 nextState = BackupState.FINAL; 3242 } 3243 3244 executeNextState(nextState); 3245 } 3246 3247 @Override 3248 public void handleTimeout() { 3249 // Whoops, the current agent timed out running doBackup(). Tidy up and restage 3250 // it for the next time we run a backup pass. 3251 // !!! TODO: keep track of failure counts per agent, and blacklist those which 3252 // fail repeatedly (i.e. have proved themselves to be buggy). 3253 Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName); 3254 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName, 3255 "timeout"); 3256 addBackupTrace("timeout of " + mCurrentPackage.packageName); 3257 agentErrorCleanup(); 3258 dataChangedImpl(mCurrentPackage.packageName); 3259 } 3260 3261 void revertAndEndBackup() { 3262 if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything"); 3263 addBackupTrace("transport error; reverting"); 3264 3265 // We want to reset the backup schedule based on whatever the transport suggests 3266 // by way of retry/backoff time. 3267 long delay; 3268 try { 3269 delay = mTransport.requestBackupTime(); 3270 } catch (Exception e) { 3271 Slog.w(TAG, "Unable to contact transport for recommended backoff"); 3272 delay = 0; // use the scheduler's default 3273 } 3274 KeyValueBackupJob.schedule(mContext, delay); 3275 3276 for (BackupRequest request : mOriginalQueue) { 3277 dataChangedImpl(request.packageName); 3278 } 3279 3280 } 3281 3282 void agentErrorCleanup() { 3283 mBackupDataName.delete(); 3284 mNewStateName.delete(); 3285 clearAgentState(); 3286 3287 executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE); 3288 } 3289 3290 // Cleanup common to both success and failure cases 3291 void clearAgentState() { 3292 try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {} 3293 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 3294 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 3295 synchronized (mCurrentOpLock) { 3296 // Current-operation callback handling requires the validity of these various 3297 // bits of internal state as an invariant of the operation still being live. 3298 // This means we make sure to clear all of the state in unison inside the lock. 3299 mCurrentOperations.clear(); 3300 mSavedState = mBackupData = mNewState = null; 3301 } 3302 3303 // If this was a pseudopackage there's no associated Activity Manager state 3304 if (mCurrentPackage.applicationInfo != null) { 3305 addBackupTrace("unbinding " + mCurrentPackage.packageName); 3306 try { // unbind even on timeout, just in case 3307 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 3308 } catch (RemoteException e) { /* can't happen; activity manager is local */ } 3309 } 3310 } 3311 3312 void executeNextState(BackupState nextState) { 3313 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 3314 + this + " nextState=" + nextState); 3315 addBackupTrace("executeNextState => " + nextState); 3316 mCurrentState = nextState; 3317 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 3318 mBackupHandler.sendMessage(msg); 3319 } 3320 } 3321 3322 3323 // ----- Full backup/restore to a file/socket ----- 3324 3325 class FullBackupObbConnection implements ServiceConnection { 3326 volatile IObbBackupService mService; 3327 3328 FullBackupObbConnection() { 3329 mService = null; 3330 } 3331 3332 public void establish() { 3333 if (MORE_DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this); 3334 Intent obbIntent = new Intent().setComponent(new ComponentName( 3335 "com.android.sharedstoragebackup", 3336 "com.android.sharedstoragebackup.ObbBackupService")); 3337 BackupManagerService.this.mContext.bindService( 3338 obbIntent, this, Context.BIND_AUTO_CREATE); 3339 } 3340 3341 public void tearDown() { 3342 BackupManagerService.this.mContext.unbindService(this); 3343 } 3344 3345 public boolean backupObbs(PackageInfo pkg, OutputStream out) { 3346 boolean success = false; 3347 waitForConnection(); 3348 3349 ParcelFileDescriptor[] pipes = null; 3350 try { 3351 pipes = ParcelFileDescriptor.createPipe(); 3352 int token = generateToken(); 3353 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 3354 mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder); 3355 routeSocketDataToOutput(pipes[0], out); 3356 success = waitUntilOperationComplete(token); 3357 } catch (Exception e) { 3358 Slog.w(TAG, "Unable to back up OBBs for " + pkg, e); 3359 } finally { 3360 try { 3361 out.flush(); 3362 if (pipes != null) { 3363 if (pipes[0] != null) pipes[0].close(); 3364 if (pipes[1] != null) pipes[1].close(); 3365 } 3366 } catch (IOException e) { 3367 Slog.w(TAG, "I/O error closing down OBB backup", e); 3368 } 3369 } 3370 return success; 3371 } 3372 3373 public void restoreObbFile(String pkgName, ParcelFileDescriptor data, 3374 long fileSize, int type, String path, long mode, long mtime, 3375 int token, IBackupManager callbackBinder) { 3376 waitForConnection(); 3377 3378 try { 3379 mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime, 3380 token, callbackBinder); 3381 } catch (Exception e) { 3382 Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e); 3383 } 3384 } 3385 3386 private void waitForConnection() { 3387 synchronized (this) { 3388 while (mService == null) { 3389 if (MORE_DEBUG) Slog.i(TAG, "...waiting for OBB service binding..."); 3390 try { 3391 this.wait(); 3392 } catch (InterruptedException e) { /* never interrupted */ } 3393 } 3394 if (MORE_DEBUG) Slog.i(TAG, "Connected to OBB service; continuing"); 3395 } 3396 } 3397 3398 @Override 3399 public void onServiceConnected(ComponentName name, IBinder service) { 3400 synchronized (this) { 3401 mService = IObbBackupService.Stub.asInterface(service); 3402 if (MORE_DEBUG) Slog.i(TAG, "OBB service connection " + mService 3403 + " connected on " + this); 3404 this.notifyAll(); 3405 } 3406 } 3407 3408 @Override 3409 public void onServiceDisconnected(ComponentName name) { 3410 synchronized (this) { 3411 mService = null; 3412 if (MORE_DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this); 3413 this.notifyAll(); 3414 } 3415 } 3416 3417 } 3418 3419 private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out) 3420 throws IOException { 3421 FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor()); 3422 DataInputStream in = new DataInputStream(raw); 3423 3424 byte[] buffer = new byte[32 * 1024]; 3425 int chunkTotal; 3426 while ((chunkTotal = in.readInt()) > 0) { 3427 while (chunkTotal > 0) { 3428 int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal; 3429 int nRead = in.read(buffer, 0, toRead); 3430 out.write(buffer, 0, nRead); 3431 chunkTotal -= nRead; 3432 } 3433 } 3434 } 3435 3436 void tearDownAgentAndKill(ApplicationInfo app) { 3437 try { 3438 // unbind and tidy up even on timeout or failure, just in case 3439 mActivityManager.unbindBackupAgent(app); 3440 3441 // The agent was running with a stub Application object, so shut it down. 3442 // !!! We hardcode the confirmation UI's package name here rather than use a 3443 // manifest flag! TODO something less direct. 3444 if (app.uid != Process.SYSTEM_UID 3445 && !app.packageName.equals("com.android.backupconfirm") 3446 && app.uid != Process.PHONE_UID) { 3447 if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process"); 3448 mActivityManager.killApplicationProcess(app.processName, app.uid); 3449 } else { 3450 if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName); 3451 } 3452 } catch (RemoteException e) { 3453 Slog.d(TAG, "Lost app trying to shut down"); 3454 } 3455 } 3456 3457 // Core logic for performing one package's full backup, gathering the tarball from the 3458 // application and emitting it to the designated OutputStream. 3459 3460 // Callout from the engine to an interested participant that might need to communicate 3461 // with the agent prior to asking it to move data 3462 interface FullBackupPreflight { 3463 /** 3464 * Perform the preflight operation necessary for the given package. 3465 * @param pkg The name of the package being proposed for full-data backup 3466 * @param agent Live BackupAgent binding to the target app's agent 3467 * @return BackupTransport.TRANSPORT_OK to proceed with the backup operation, 3468 * or one of the other BackupTransport.* error codes as appropriate 3469 */ 3470 int preflightFullBackup(PackageInfo pkg, IBackupAgent agent); 3471 3472 long expectedSize(); 3473 }; 3474 3475 class FullBackupEngine { 3476 OutputStream mOutput; 3477 FullBackupPreflight mPreflightHook; 3478 IFullBackupRestoreObserver mObserver; 3479 File mFilesDir; 3480 File mManifestFile; 3481 File mMetadataFile; 3482 boolean mIncludeApks; 3483 3484 class FullBackupRunner implements Runnable { 3485 PackageInfo mPackage; 3486 byte[] mWidgetData; 3487 IBackupAgent mAgent; 3488 ParcelFileDescriptor mPipe; 3489 int mToken; 3490 boolean mSendApk; 3491 boolean mWriteManifest; 3492 3493 FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe, 3494 int token, boolean sendApk, boolean writeManifest, byte[] widgetData) 3495 throws IOException { 3496 mPackage = pack; 3497 mWidgetData = widgetData; 3498 mAgent = agent; 3499 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor()); 3500 mToken = token; 3501 mSendApk = sendApk; 3502 mWriteManifest = writeManifest; 3503 } 3504 3505 @Override 3506 public void run() { 3507 try { 3508 FullBackupDataOutput output = new FullBackupDataOutput(mPipe); 3509 3510 if (mWriteManifest) { 3511 final boolean writeWidgetData = mWidgetData != null; 3512 if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName); 3513 writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData); 3514 FullBackup.backupToTar(mPackage.packageName, null, null, 3515 mFilesDir.getAbsolutePath(), 3516 mManifestFile.getAbsolutePath(), 3517 output); 3518 mManifestFile.delete(); 3519 3520 // We only need to write a metadata file if we have widget data to stash 3521 if (writeWidgetData) { 3522 writeMetadata(mPackage, mMetadataFile, mWidgetData); 3523 FullBackup.backupToTar(mPackage.packageName, null, null, 3524 mFilesDir.getAbsolutePath(), 3525 mMetadataFile.getAbsolutePath(), 3526 output); 3527 mMetadataFile.delete(); 3528 } 3529 } 3530 3531 if (mSendApk) { 3532 writeApkToBackup(mPackage, output); 3533 } 3534 3535 if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName); 3536 prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null); 3537 mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder); 3538 } catch (IOException e) { 3539 Slog.e(TAG, "Error running full backup for " + mPackage.packageName); 3540 } catch (RemoteException e) { 3541 Slog.e(TAG, "Remote agent vanished during full backup of " 3542 + mPackage.packageName); 3543 } finally { 3544 try { 3545 mPipe.close(); 3546 } catch (IOException e) {} 3547 } 3548 } 3549 } 3550 3551 FullBackupEngine(OutputStream output, String packageName, FullBackupPreflight preflightHook, 3552 boolean alsoApks) { 3553 mOutput = output; 3554 mPreflightHook = preflightHook; 3555 mIncludeApks = alsoApks; 3556 mFilesDir = new File("/data/system"); 3557 mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME); 3558 mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME); 3559 } 3560 3561 public int backupOnePackage(PackageInfo pkg) throws RemoteException { 3562 int result = BackupTransport.TRANSPORT_OK; 3563 Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName); 3564 3565 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo, 3566 IApplicationThread.BACKUP_MODE_FULL); 3567 if (agent != null) { 3568 ParcelFileDescriptor[] pipes = null; 3569 try { 3570 // Call the preflight hook, if any 3571 if (mPreflightHook != null) { 3572 result = mPreflightHook.preflightFullBackup(pkg, agent); 3573 if (MORE_DEBUG) { 3574 Slog.v(TAG, "preflight returned " + result); 3575 } 3576 } 3577 3578 // If we're still good to go after preflighting, start moving data 3579 if (result == BackupTransport.TRANSPORT_OK) { 3580 pipes = ParcelFileDescriptor.createPipe(); 3581 3582 ApplicationInfo app = pkg.applicationInfo; 3583 final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE); 3584 final boolean sendApk = mIncludeApks 3585 && !isSharedStorage 3586 && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0) 3587 && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 || 3588 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); 3589 3590 // TODO: http://b/22388012 3591 byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName, 3592 UserHandle.USER_SYSTEM); 3593 3594 final int token = generateToken(); 3595 FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1], 3596 token, sendApk, !isSharedStorage, widgetBlob); 3597 pipes[1].close(); // the runner has dup'd it 3598 pipes[1] = null; 3599 Thread t = new Thread(runner, "app-data-runner"); 3600 t.start(); 3601 3602 // Now pull data from the app and stuff it into the output 3603 try { 3604 routeSocketDataToOutput(pipes[0], mOutput); 3605 } catch (IOException e) { 3606 Slog.i(TAG, "Caught exception reading from agent", e); 3607 result = BackupTransport.AGENT_ERROR; 3608 } 3609 3610 if (!waitUntilOperationComplete(token)) { 3611 Slog.e(TAG, "Full backup failed on package " + pkg.packageName); 3612 result = BackupTransport.AGENT_ERROR; 3613 } else { 3614 if (MORE_DEBUG) { 3615 Slog.d(TAG, "Full package backup success: " + pkg.packageName); 3616 } 3617 } 3618 } 3619 } catch (IOException e) { 3620 Slog.e(TAG, "Error backing up " + pkg.packageName, e); 3621 result = BackupTransport.AGENT_ERROR; 3622 } finally { 3623 try { 3624 // flush after every package 3625 mOutput.flush(); 3626 if (pipes != null) { 3627 if (pipes[0] != null) pipes[0].close(); 3628 if (pipes[1] != null) pipes[1].close(); 3629 } 3630 } catch (IOException e) { 3631 Slog.w(TAG, "Error bringing down backup stack"); 3632 result = BackupTransport.TRANSPORT_ERROR; 3633 } 3634 } 3635 } else { 3636 Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName); 3637 result = BackupTransport.AGENT_ERROR; 3638 } 3639 tearDown(pkg); 3640 return result; 3641 } 3642 3643 private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) { 3644 // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here 3645 // TODO: handle backing up split APKs 3646 final String appSourceDir = pkg.applicationInfo.getBaseCodePath(); 3647 final String apkDir = new File(appSourceDir).getParent(); 3648 FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null, 3649 apkDir, appSourceDir, output); 3650 3651 // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM 3652 // doesn't have access to external storage. 3653 3654 // Save associated .obb content if it exists and we did save the apk 3655 // check for .obb and save those too 3656 // TODO: http://b/22388012 3657 final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_SYSTEM); 3658 final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0]; 3659 if (obbDir != null) { 3660 if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); 3661 File[] obbFiles = obbDir.listFiles(); 3662 if (obbFiles != null) { 3663 final String obbDirName = obbDir.getAbsolutePath(); 3664 for (File obb : obbFiles) { 3665 FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null, 3666 obbDirName, obb.getAbsolutePath(), output); 3667 } 3668 } 3669 } 3670 } 3671 3672 private void writeAppManifest(PackageInfo pkg, File manifestFile, 3673 boolean withApk, boolean withWidgets) throws IOException { 3674 // Manifest format. All data are strings ending in LF: 3675 // BACKUP_MANIFEST_VERSION, currently 1 3676 // 3677 // Version 1: 3678 // package name 3679 // package's versionCode 3680 // platform versionCode 3681 // getInstallerPackageName() for this package (maybe empty) 3682 // boolean: "1" if archive includes .apk; any other string means not 3683 // number of signatures == N 3684 // N*: signature byte array in ascii format per Signature.toCharsString() 3685 StringBuilder builder = new StringBuilder(4096); 3686 StringBuilderPrinter printer = new StringBuilderPrinter(builder); 3687 3688 printer.println(Integer.toString(BACKUP_MANIFEST_VERSION)); 3689 printer.println(pkg.packageName); 3690 printer.println(Integer.toString(pkg.versionCode)); 3691 printer.println(Integer.toString(Build.VERSION.SDK_INT)); 3692 3693 String installerName = mPackageManager.getInstallerPackageName(pkg.packageName); 3694 printer.println((installerName != null) ? installerName : ""); 3695 3696 printer.println(withApk ? "1" : "0"); 3697 if (pkg.signatures == null) { 3698 printer.println("0"); 3699 } else { 3700 printer.println(Integer.toString(pkg.signatures.length)); 3701 for (Signature sig : pkg.signatures) { 3702 printer.println(sig.toCharsString()); 3703 } 3704 } 3705 3706 FileOutputStream outstream = new FileOutputStream(manifestFile); 3707 outstream.write(builder.toString().getBytes()); 3708 outstream.close(); 3709 3710 // We want the manifest block in the archive stream to be idempotent: 3711 // each time we generate a backup stream for the app, we want the manifest 3712 // block to be identical. The underlying tar mechanism sees it as a file, 3713 // though, and will propagate its mtime, causing the tar header to vary. 3714 // Avoid this problem by pinning the mtime to zero. 3715 manifestFile.setLastModified(0); 3716 } 3717 3718 // Widget metadata format. All header entries are strings ending in LF: 3719 // 3720 // Version 1 header: 3721 // BACKUP_METADATA_VERSION, currently "1" 3722 // package name 3723 // 3724 // File data (all integers are binary in network byte order) 3725 // *N: 4 : integer token identifying which metadata blob 3726 // 4 : integer size of this blob = N 3727 // N : raw bytes of this metadata blob 3728 // 3729 // Currently understood blobs (always in network byte order): 3730 // 3731 // widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN) 3732 // 3733 // Unrecognized blobs are *ignored*, not errors. 3734 private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData) 3735 throws IOException { 3736 StringBuilder b = new StringBuilder(512); 3737 StringBuilderPrinter printer = new StringBuilderPrinter(b); 3738 printer.println(Integer.toString(BACKUP_METADATA_VERSION)); 3739 printer.println(pkg.packageName); 3740 3741 FileOutputStream fout = new FileOutputStream(destination); 3742 BufferedOutputStream bout = new BufferedOutputStream(fout); 3743 DataOutputStream out = new DataOutputStream(bout); 3744 bout.write(b.toString().getBytes()); // bypassing DataOutputStream 3745 3746 if (widgetData != null && widgetData.length > 0) { 3747 out.writeInt(BACKUP_WIDGET_METADATA_TOKEN); 3748 out.writeInt(widgetData.length); 3749 out.write(widgetData); 3750 } 3751 bout.flush(); 3752 out.close(); 3753 3754 // As with the manifest file, guarantee idempotence of the archive metadata 3755 // for the widget block by using a fixed mtime on the transient file. 3756 destination.setLastModified(0); 3757 } 3758 3759 private void tearDown(PackageInfo pkg) { 3760 if (pkg != null) { 3761 final ApplicationInfo app = pkg.applicationInfo; 3762 if (app != null) { 3763 tearDownAgentAndKill(app); 3764 } 3765 } 3766 } 3767 } 3768 3769 // Generic driver skeleton for full backup operations 3770 abstract class FullBackupTask implements Runnable { 3771 IFullBackupRestoreObserver mObserver; 3772 3773 FullBackupTask(IFullBackupRestoreObserver observer) { 3774 mObserver = observer; 3775 } 3776 3777 // wrappers for observer use 3778 final void sendStartBackup() { 3779 if (mObserver != null) { 3780 try { 3781 mObserver.onStartBackup(); 3782 } catch (RemoteException e) { 3783 Slog.w(TAG, "full backup observer went away: startBackup"); 3784 mObserver = null; 3785 } 3786 } 3787 } 3788 3789 final void sendOnBackupPackage(String name) { 3790 if (mObserver != null) { 3791 try { 3792 // TODO: use a more user-friendly name string 3793 mObserver.onBackupPackage(name); 3794 } catch (RemoteException e) { 3795 Slog.w(TAG, "full backup observer went away: backupPackage"); 3796 mObserver = null; 3797 } 3798 } 3799 } 3800 3801 final void sendEndBackup() { 3802 if (mObserver != null) { 3803 try { 3804 mObserver.onEndBackup(); 3805 } catch (RemoteException e) { 3806 Slog.w(TAG, "full backup observer went away: endBackup"); 3807 mObserver = null; 3808 } 3809 } 3810 } 3811 } 3812 3813 boolean deviceIsEncrypted() { 3814 try { 3815 return mMountService.getEncryptionState() 3816 != IMountService.ENCRYPTION_STATE_NONE 3817 && mMountService.getPasswordType() 3818 != StorageManager.CRYPT_TYPE_DEFAULT; 3819 } catch (Exception e) { 3820 // If we can't talk to the mount service we have a serious problem; fail 3821 // "secure" i.e. assuming that the device is encrypted. 3822 Slog.e(TAG, "Unable to communicate with mount service: " + e.getMessage()); 3823 return true; 3824 } 3825 } 3826 3827 // Full backup task variant used for adb backup 3828 class PerformAdbBackupTask extends FullBackupTask { 3829 FullBackupEngine mBackupEngine; 3830 final AtomicBoolean mLatch; 3831 3832 ParcelFileDescriptor mOutputFile; 3833 DeflaterOutputStream mDeflater; 3834 boolean mIncludeApks; 3835 boolean mIncludeObbs; 3836 boolean mIncludeShared; 3837 boolean mDoWidgets; 3838 boolean mAllApps; 3839 boolean mIncludeSystem; 3840 boolean mCompress; 3841 ArrayList<String> mPackages; 3842 String mCurrentPassword; 3843 String mEncryptPassword; 3844 3845 PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, 3846 boolean includeApks, boolean includeObbs, boolean includeShared, 3847 boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps, 3848 boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch) { 3849 super(observer); 3850 mLatch = latch; 3851 3852 mOutputFile = fd; 3853 mIncludeApks = includeApks; 3854 mIncludeObbs = includeObbs; 3855 mIncludeShared = includeShared; 3856 mDoWidgets = doWidgets; 3857 mAllApps = doAllApps; 3858 mIncludeSystem = doSystem; 3859 mPackages = (packages == null) 3860 ? new ArrayList<String>() 3861 : new ArrayList<String>(Arrays.asList(packages)); 3862 mCurrentPassword = curPassword; 3863 // when backing up, if there is a current backup password, we require that 3864 // the user use a nonempty encryption password as well. if one is supplied 3865 // in the UI we use that, but if the UI was left empty we fall back to the 3866 // current backup password (which was supplied by the user as well). 3867 if (encryptPassword == null || "".equals(encryptPassword)) { 3868 mEncryptPassword = curPassword; 3869 } else { 3870 mEncryptPassword = encryptPassword; 3871 } 3872 mCompress = doCompress; 3873 } 3874 3875 void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) { 3876 for (String pkgName : pkgNames) { 3877 if (!set.containsKey(pkgName)) { 3878 try { 3879 PackageInfo info = mPackageManager.getPackageInfo(pkgName, 3880 PackageManager.GET_SIGNATURES); 3881 set.put(pkgName, info); 3882 } catch (NameNotFoundException e) { 3883 Slog.w(TAG, "Unknown package " + pkgName + ", skipping"); 3884 } 3885 } 3886 } 3887 } 3888 3889 private OutputStream emitAesBackupHeader(StringBuilder headerbuf, 3890 OutputStream ofstream) throws Exception { 3891 // User key will be used to encrypt the master key. 3892 byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE); 3893 SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt, 3894 PBKDF2_HASH_ROUNDS); 3895 3896 // the master key is random for each backup 3897 byte[] masterPw = new byte[256 / 8]; 3898 mRng.nextBytes(masterPw); 3899 byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE); 3900 3901 // primary encryption of the datastream with the random key 3902 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 3903 SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES"); 3904 c.init(Cipher.ENCRYPT_MODE, masterKeySpec); 3905 OutputStream finalOutput = new CipherOutputStream(ofstream, c); 3906 3907 // line 4: name of encryption algorithm 3908 headerbuf.append(ENCRYPTION_ALGORITHM_NAME); 3909 headerbuf.append('\n'); 3910 // line 5: user password salt [hex] 3911 headerbuf.append(byteArrayToHex(newUserSalt)); 3912 headerbuf.append('\n'); 3913 // line 6: master key checksum salt [hex] 3914 headerbuf.append(byteArrayToHex(checksumSalt)); 3915 headerbuf.append('\n'); 3916 // line 7: number of PBKDF2 rounds used [decimal] 3917 headerbuf.append(PBKDF2_HASH_ROUNDS); 3918 headerbuf.append('\n'); 3919 3920 // line 8: IV of the user key [hex] 3921 Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding"); 3922 mkC.init(Cipher.ENCRYPT_MODE, userKey); 3923 3924 byte[] IV = mkC.getIV(); 3925 headerbuf.append(byteArrayToHex(IV)); 3926 headerbuf.append('\n'); 3927 3928 // line 9: master IV + key blob, encrypted by the user key [hex]. Blob format: 3929 // [byte] IV length = Niv 3930 // [array of Niv bytes] IV itself 3931 // [byte] master key length = Nmk 3932 // [array of Nmk bytes] master key itself 3933 // [byte] MK checksum hash length = Nck 3934 // [array of Nck bytes] master key checksum hash 3935 // 3936 // The checksum is the (master key + checksum salt), run through the 3937 // stated number of PBKDF2 rounds 3938 IV = c.getIV(); 3939 byte[] mk = masterKeySpec.getEncoded(); 3940 byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(), 3941 checksumSalt, PBKDF2_HASH_ROUNDS); 3942 3943 ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length 3944 + checksum.length + 3); 3945 DataOutputStream mkOut = new DataOutputStream(blob); 3946 mkOut.writeByte(IV.length); 3947 mkOut.write(IV); 3948 mkOut.writeByte(mk.length); 3949 mkOut.write(mk); 3950 mkOut.writeByte(checksum.length); 3951 mkOut.write(checksum); 3952 mkOut.flush(); 3953 byte[] encryptedMk = mkC.doFinal(blob.toByteArray()); 3954 headerbuf.append(byteArrayToHex(encryptedMk)); 3955 headerbuf.append('\n'); 3956 3957 return finalOutput; 3958 } 3959 3960 private void finalizeBackup(OutputStream out) { 3961 try { 3962 // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes. 3963 byte[] eof = new byte[512 * 2]; // newly allocated == zero filled 3964 out.write(eof); 3965 } catch (IOException e) { 3966 Slog.w(TAG, "Error attempting to finalize backup stream"); 3967 } 3968 } 3969 3970 @Override 3971 public void run() { 3972 Slog.i(TAG, "--- Performing full-dataset adb backup ---"); 3973 3974 TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>(); 3975 FullBackupObbConnection obbConnection = new FullBackupObbConnection(); 3976 obbConnection.establish(); // we'll want this later 3977 3978 sendStartBackup(); 3979 3980 // doAllApps supersedes the package set if any 3981 if (mAllApps) { 3982 List<PackageInfo> allPackages = mPackageManager.getInstalledPackages( 3983 PackageManager.GET_SIGNATURES); 3984 for (int i = 0; i < allPackages.size(); i++) { 3985 PackageInfo pkg = allPackages.get(i); 3986 // Exclude system apps if we've been asked to do so 3987 if (mIncludeSystem == true 3988 || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) { 3989 packagesToBackup.put(pkg.packageName, pkg); 3990 } 3991 } 3992 } 3993 3994 // If we're doing widget state as well, ensure that we have all the involved 3995 // host & provider packages in the set 3996 if (mDoWidgets) { 3997 // TODO: http://b/22388012 3998 List<String> pkgs = 3999 AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_SYSTEM); 4000 if (pkgs != null) { 4001 if (MORE_DEBUG) { 4002 Slog.i(TAG, "Adding widget participants to backup set:"); 4003 StringBuilder sb = new StringBuilder(128); 4004 sb.append(" "); 4005 for (String s : pkgs) { 4006 sb.append(' '); 4007 sb.append(s); 4008 } 4009 Slog.i(TAG, sb.toString()); 4010 } 4011 addPackagesToSet(packagesToBackup, pkgs); 4012 } 4013 } 4014 4015 // Now process the command line argument packages, if any. Note that explicitly- 4016 // named system-partition packages will be included even if includeSystem was 4017 // set to false. 4018 if (mPackages != null) { 4019 addPackagesToSet(packagesToBackup, mPackages); 4020 } 4021 4022 // Now we cull any inapplicable / inappropriate packages from the set. This 4023 // includes the special shared-storage agent package; we handle that one 4024 // explicitly at the end of the backup pass. 4025 Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator(); 4026 while (iter.hasNext()) { 4027 PackageInfo pkg = iter.next().getValue(); 4028 if (!appIsEligibleForBackup(pkg.applicationInfo)) { 4029 iter.remove(); 4030 } 4031 } 4032 4033 // flatten the set of packages now so we can explicitly control the ordering 4034 ArrayList<PackageInfo> backupQueue = 4035 new ArrayList<PackageInfo>(packagesToBackup.values()); 4036 FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor()); 4037 OutputStream out = null; 4038 4039 PackageInfo pkg = null; 4040 try { 4041 boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0); 4042 4043 // Only allow encrypted backups of encrypted devices 4044 if (deviceIsEncrypted() && !encrypting) { 4045 Slog.e(TAG, "Unencrypted backup of encrypted device; aborting"); 4046 return; 4047 } 4048 4049 OutputStream finalOutput = ofstream; 4050 4051 // Verify that the given password matches the currently-active 4052 // backup password, if any 4053 if (!backupPasswordMatches(mCurrentPassword)) { 4054 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 4055 return; 4056 } 4057 4058 // Write the global file header. All strings are UTF-8 encoded; lines end 4059 // with a '\n' byte. Actual backup data begins immediately following the 4060 // final '\n'. 4061 // 4062 // line 1: "ANDROID BACKUP" 4063 // line 2: backup file format version, currently "2" 4064 // line 3: compressed? "0" if not compressed, "1" if compressed. 4065 // line 4: name of encryption algorithm [currently only "none" or "AES-256"] 4066 // 4067 // When line 4 is not "none", then additional header data follows: 4068 // 4069 // line 5: user password salt [hex] 4070 // line 6: master key checksum salt [hex] 4071 // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal] 4072 // line 8: IV of the user key [hex] 4073 // line 9: master key blob [hex] 4074 // IV of the master key, master key itself, master key checksum hash 4075 // 4076 // The master key checksum is the master key plus its checksum salt, run through 4077 // 10k rounds of PBKDF2. This is used to verify that the user has supplied the 4078 // correct password for decrypting the archive: the master key decrypted from 4079 // the archive using the user-supplied password is also run through PBKDF2 in 4080 // this way, and if the result does not match the checksum as stored in the 4081 // archive, then we know that the user-supplied password does not match the 4082 // archive's. 4083 StringBuilder headerbuf = new StringBuilder(1024); 4084 4085 headerbuf.append(BACKUP_FILE_HEADER_MAGIC); 4086 headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n 4087 headerbuf.append(mCompress ? "\n1\n" : "\n0\n"); 4088 4089 try { 4090 // Set up the encryption stage if appropriate, and emit the correct header 4091 if (encrypting) { 4092 finalOutput = emitAesBackupHeader(headerbuf, finalOutput); 4093 } else { 4094 headerbuf.append("none\n"); 4095 } 4096 4097 byte[] header = headerbuf.toString().getBytes("UTF-8"); 4098 ofstream.write(header); 4099 4100 // Set up the compression stage feeding into the encryption stage (if any) 4101 if (mCompress) { 4102 Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION); 4103 finalOutput = new DeflaterOutputStream(finalOutput, deflater, true); 4104 } 4105 4106 out = finalOutput; 4107 } catch (Exception e) { 4108 // Should never happen! 4109 Slog.e(TAG, "Unable to emit archive header", e); 4110 return; 4111 } 4112 4113 // Shared storage if requested 4114 if (mIncludeShared) { 4115 try { 4116 pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0); 4117 backupQueue.add(pkg); 4118 } catch (NameNotFoundException e) { 4119 Slog.e(TAG, "Unable to find shared-storage backup handler"); 4120 } 4121 } 4122 4123 // Now actually run the constructed backup sequence 4124 int N = backupQueue.size(); 4125 for (int i = 0; i < N; i++) { 4126 pkg = backupQueue.get(i); 4127 final boolean isSharedStorage = 4128 pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE); 4129 4130 mBackupEngine = new FullBackupEngine(out, pkg.packageName, null, mIncludeApks); 4131 sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName); 4132 mBackupEngine.backupOnePackage(pkg); 4133 4134 // after the app's agent runs to handle its private filesystem 4135 // contents, back up any OBB content it has on its behalf. 4136 if (mIncludeObbs) { 4137 boolean obbOkay = obbConnection.backupObbs(pkg, out); 4138 if (!obbOkay) { 4139 throw new RuntimeException("Failure writing OBB stack for " + pkg); 4140 } 4141 } 4142 } 4143 4144 // Done! 4145 finalizeBackup(out); 4146 } catch (RemoteException e) { 4147 Slog.e(TAG, "App died during full backup"); 4148 } catch (Exception e) { 4149 Slog.e(TAG, "Internal exception during full backup", e); 4150 } finally { 4151 try { 4152 if (out != null) out.close(); 4153 mOutputFile.close(); 4154 } catch (IOException e) { 4155 /* nothing we can do about this */ 4156 } 4157 synchronized (mCurrentOpLock) { 4158 mCurrentOperations.clear(); 4159 } 4160 synchronized (mLatch) { 4161 mLatch.set(true); 4162 mLatch.notifyAll(); 4163 } 4164 sendEndBackup(); 4165 obbConnection.tearDown(); 4166 if (DEBUG) Slog.d(TAG, "Full backup pass complete."); 4167 mWakelock.release(); 4168 } 4169 } 4170 } 4171 4172 // Full backup task extension used for transport-oriented operation 4173 class PerformFullTransportBackupTask extends FullBackupTask { 4174 static final String TAG = "PFTBT"; 4175 ArrayList<PackageInfo> mPackages; 4176 boolean mUpdateSchedule; 4177 CountDownLatch mLatch; 4178 AtomicBoolean mKeepRunning; // signal from job scheduler 4179 FullBackupJob mJob; // if a scheduled job needs to be finished afterwards 4180 IBackupObserver mBackupObserver; 4181 boolean mUserInitiated; 4182 4183 PerformFullTransportBackupTask(IFullBackupRestoreObserver observer, 4184 String[] whichPackages, boolean updateSchedule, 4185 FullBackupJob runningJob, CountDownLatch latch, IBackupObserver backupObserver, 4186 boolean userInitiated) { 4187 super(observer); 4188 mUpdateSchedule = updateSchedule; 4189 mLatch = latch; 4190 mKeepRunning = new AtomicBoolean(true); 4191 mJob = runningJob; 4192 mPackages = new ArrayList<PackageInfo>(whichPackages.length); 4193 mBackupObserver = backupObserver; 4194 mUserInitiated = userInitiated; 4195 4196 for (String pkg : whichPackages) { 4197 try { 4198 PackageInfo info = mPackageManager.getPackageInfo(pkg, 4199 PackageManager.GET_SIGNATURES); 4200 if (!appIsEligibleForBackup(info.applicationInfo)) { 4201 // Cull any packages that have indicated that backups are not permitted, 4202 // that run as system-domain uids but do not define their own backup agents, 4203 // as well as any explicit mention of the 'special' shared-storage agent 4204 // package (we handle that one at the end). 4205 if (MORE_DEBUG) { 4206 Slog.d(TAG, "Ignoring ineligible package " + pkg); 4207 } 4208 sendBackupOnResult(mBackupObserver, pkg, 4209 BackupManager.ERROR_BACKUP_NOT_ALLOWED); 4210 continue; 4211 } else if (!appGetsFullBackup(info)) { 4212 // Cull any packages that are found in the queue but now aren't supposed 4213 // to get full-data backup operations. 4214 if (MORE_DEBUG) { 4215 Slog.d(TAG, "Ignoring full-data backup of key/value participant " 4216 + pkg); 4217 } 4218 sendBackupOnResult(mBackupObserver, pkg, 4219 BackupManager.ERROR_BACKUP_NOT_ALLOWED); 4220 continue; 4221 } else if (appIsStopped(info.applicationInfo)) { 4222 // Cull any packages in the 'stopped' state: they've either just been 4223 // installed or have explicitly been force-stopped by the user. In both 4224 // cases we do not want to launch them for backup. 4225 if (MORE_DEBUG) { 4226 Slog.d(TAG, "Ignoring stopped package " + pkg); 4227 } 4228 sendBackupOnResult(mBackupObserver, pkg, 4229 BackupManager.ERROR_BACKUP_NOT_ALLOWED); 4230 continue; 4231 } 4232 mPackages.add(info); 4233 } catch (NameNotFoundException e) { 4234 Slog.i(TAG, "Requested package " + pkg + " not found; ignoring"); 4235 } 4236 } 4237 } 4238 4239 public void setRunning(boolean running) { 4240 mKeepRunning.set(running); 4241 } 4242 4243 @Override 4244 public void run() { 4245 // data from the app, passed to us for bridging to the transport 4246 ParcelFileDescriptor[] enginePipes = null; 4247 4248 // Pipe through which we write data to the transport 4249 ParcelFileDescriptor[] transportPipes = null; 4250 4251 PackageInfo currentPackage; 4252 long backoff = 0; 4253 4254 try { 4255 if (!mEnabled || !mProvisioned) { 4256 // Backups are globally disabled, so don't proceed. 4257 if (DEBUG) { 4258 Slog.i(TAG, "full backup requested but e=" + mEnabled 4259 + " p=" + mProvisioned + "; ignoring"); 4260 } 4261 mUpdateSchedule = false; 4262 sendBackupFinished(mBackupObserver, BackupManager.ERROR_BACKUP_NOT_ALLOWED); 4263 return; 4264 } 4265 4266 IBackupTransport transport = getTransport(mCurrentTransport); 4267 if (transport == null) { 4268 Slog.w(TAG, "Transport not present; full data backup not performed"); 4269 sendBackupFinished(mBackupObserver, BackupManager.ERROR_TRANSPORT_ABORTED); 4270 return; 4271 } 4272 4273 // Set up to send data to the transport 4274 final int N = mPackages.size(); 4275 final byte[] buffer = new byte[8192]; 4276 for (int i = 0; i < N; i++) { 4277 currentPackage = mPackages.get(i); 4278 if (DEBUG) { 4279 Slog.i(TAG, "Initiating full-data transport backup of " 4280 + currentPackage.packageName); 4281 } 4282 EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, 4283 currentPackage.packageName); 4284 4285 transportPipes = ParcelFileDescriptor.createPipe(); 4286 4287 // Tell the transport the data's coming 4288 int flags = mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0; 4289 int result = transport.performFullBackup(currentPackage, 4290 transportPipes[0], flags); 4291 if (result == BackupTransport.TRANSPORT_OK) { 4292 // The transport has its own copy of the read end of the pipe, 4293 // so close ours now 4294 transportPipes[0].close(); 4295 transportPipes[0] = null; 4296 4297 // Now set up the backup engine / data source end of things 4298 enginePipes = ParcelFileDescriptor.createPipe(); 4299 CountDownLatch runnerLatch = new CountDownLatch(1); 4300 SinglePackageBackupRunner backupRunner = 4301 new SinglePackageBackupRunner(enginePipes[1], currentPackage, 4302 transport, runnerLatch); 4303 // The runner dup'd the pipe half, so we close it here 4304 enginePipes[1].close(); 4305 enginePipes[1] = null; 4306 4307 // Spin off the runner to fetch the app's data and pipe it 4308 // into the engine pipes 4309 (new Thread(backupRunner, "package-backup-bridge")).start(); 4310 4311 // Read data off the engine pipe and pass it to the transport 4312 // pipe until we hit EOD on the input stream. 4313 FileInputStream in = new FileInputStream( 4314 enginePipes[0].getFileDescriptor()); 4315 FileOutputStream out = new FileOutputStream( 4316 transportPipes[1].getFileDescriptor()); 4317 long totalRead = 0; 4318 final long expectedSize = backupRunner.expectedSize(); 4319 if (expectedSize < 0) { 4320 result = BackupTransport.AGENT_ERROR; 4321 sendBackupOnResult(mBackupObserver, currentPackage.packageName, 4322 BackupManager.ERROR_AGENT_FAILURE); 4323 } 4324 int nRead = 0; 4325 do { 4326 if (!mKeepRunning.get()) { 4327 if (DEBUG_SCHEDULING) { 4328 Slog.i(TAG, "Full backup task told to stop"); 4329 } 4330 break; 4331 } 4332 nRead = in.read(buffer); 4333 if (MORE_DEBUG) { 4334 Slog.v(TAG, "in.read(buffer) from app: " + nRead); 4335 } 4336 if (nRead > 0) { 4337 out.write(buffer, 0, nRead); 4338 result = transport.sendBackupData(nRead); 4339 totalRead += nRead; 4340 if (mBackupObserver != null && expectedSize > 0) { 4341 sendBackupOnUpdate(mBackupObserver, currentPackage.packageName, 4342 new BackupProgress(expectedSize, totalRead)); 4343 } 4344 } 4345 } while (nRead > 0 && result == BackupTransport.TRANSPORT_OK); 4346 4347 // If we've lost our running criteria, tell the transport to cancel 4348 // and roll back this (partial) backup payload; otherwise tell it 4349 // that we've reached the clean finish state. 4350 if (!mKeepRunning.get()) { 4351 result = BackupTransport.TRANSPORT_ERROR; 4352 transport.cancelFullBackup(); 4353 } else { 4354 // If we were otherwise in a good state, now interpret the final 4355 // result based on what finishBackup() returns. If we're in a 4356 // failure case already, preserve that result and ignore whatever 4357 // finishBackup() reports. 4358 final int finishResult = transport.finishBackup(); 4359 if (result == BackupTransport.TRANSPORT_OK) { 4360 result = finishResult; 4361 } 4362 } 4363 4364 if (MORE_DEBUG) { 4365 Slog.i(TAG, "Done trying to send backup data: result=" + result); 4366 } 4367 4368 if (result != BackupTransport.TRANSPORT_OK) { 4369 Slog.e(TAG, "Error " + result 4370 + " backing up " + currentPackage.packageName); 4371 } 4372 4373 // Also ask the transport how long it wants us to wait before 4374 // moving on to the next package, if any. 4375 backoff = transport.requestFullBackupTime(); 4376 if (DEBUG_SCHEDULING) { 4377 Slog.i(TAG, "Transport suggested backoff=" + backoff); 4378 } 4379 4380 } 4381 4382 // Roll this package to the end of the backup queue if we're 4383 // in a queue-driven mode (regardless of success/failure) 4384 if (mUpdateSchedule) { 4385 enqueueFullBackup(currentPackage.packageName, 4386 System.currentTimeMillis()); 4387 } 4388 4389 if (result == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { 4390 if (DEBUG) { 4391 Slog.i(TAG, "Transport rejected backup of " 4392 + currentPackage.packageName 4393 + ", skipping"); 4394 } 4395 EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, 4396 currentPackage.packageName, "transport rejected"); 4397 sendBackupOnResult(mBackupObserver, currentPackage.packageName, 4398 BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED); 4399 // do nothing, clean up, and continue looping 4400 } else if (result != BackupTransport.TRANSPORT_OK) { 4401 Slog.w(TAG, "Transport failed; aborting backup: " + result); 4402 EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE); 4403 sendBackupOnResult(mBackupObserver, currentPackage.packageName, 4404 BackupManager.ERROR_TRANSPORT_ABORTED); 4405 return; 4406 } else { 4407 // Success! 4408 EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, 4409 currentPackage.packageName); 4410 logBackupComplete(currentPackage.packageName); 4411 sendBackupOnResult(mBackupObserver, currentPackage.packageName, 4412 BackupManager.SUCCESS); 4413 } 4414 cleanUpPipes(transportPipes); 4415 cleanUpPipes(enginePipes); 4416 currentPackage = null; 4417 } 4418 4419 if (DEBUG) { 4420 Slog.i(TAG, "Full backup completed."); 4421 } 4422 sendBackupFinished(mBackupObserver, BackupManager.SUCCESS); 4423 } catch (Exception e) { 4424 Slog.w(TAG, "Exception trying full transport backup", e); 4425 sendBackupFinished(mBackupObserver, BackupManager.ERROR_TRANSPORT_ABORTED); 4426 } finally { 4427 cleanUpPipes(transportPipes); 4428 cleanUpPipes(enginePipes); 4429 4430 if (mJob != null) { 4431 mJob.finishBackupPass(); 4432 } 4433 4434 synchronized (mQueueLock) { 4435 mRunningFullBackupTask = null; 4436 } 4437 4438 4439 mLatch.countDown(); 4440 4441 // Now that we're actually done with schedule-driven work, reschedule 4442 // the next pass based on the new queue state. 4443 if (mUpdateSchedule) { 4444 scheduleNextFullBackupJob(backoff); 4445 } 4446 Slog.i(BackupManagerService.TAG, "Full data backup pass finished."); 4447 mWakelock.release(); 4448 } 4449 } 4450 4451 void cleanUpPipes(ParcelFileDescriptor[] pipes) { 4452 if (pipes != null) { 4453 if (pipes[0] != null) { 4454 ParcelFileDescriptor fd = pipes[0]; 4455 pipes[0] = null; 4456 try { 4457 fd.close(); 4458 } catch (IOException e) { 4459 Slog.w(TAG, "Unable to close pipe!"); 4460 } 4461 } 4462 if (pipes[1] != null) { 4463 ParcelFileDescriptor fd = pipes[1]; 4464 pipes[1] = null; 4465 try { 4466 fd.close(); 4467 } catch (IOException e) { 4468 Slog.w(TAG, "Unable to close pipe!"); 4469 } 4470 } 4471 } 4472 } 4473 4474 // Run the backup and pipe it back to the given socket -- expects to run on 4475 // a standalone thread. The runner owns this half of the pipe, and closes 4476 // it to indicate EOD to the other end. 4477 class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight { 4478 final AtomicInteger mResult = new AtomicInteger(); 4479 final CountDownLatch mLatch = new CountDownLatch(1); 4480 final IBackupTransport mTransport; 4481 4482 public SinglePackageBackupPreflight(IBackupTransport transport) { 4483 mTransport = transport; 4484 } 4485 4486 @Override 4487 public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) { 4488 int result; 4489 try { 4490 final int token = generateToken(); 4491 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this); 4492 addBackupTrace("preflighting"); 4493 if (MORE_DEBUG) { 4494 Slog.d(TAG, "Preflighting full payload of " + pkg.packageName); 4495 } 4496 agent.doMeasureFullBackup(token, mBackupManagerBinder); 4497 4498 // now wait to get our result back 4499 mLatch.await(); 4500 int totalSize = mResult.get(); 4501 if (MORE_DEBUG) { 4502 Slog.v(TAG, "Got preflight response; size=" + totalSize); 4503 } 4504 4505 result = mTransport.checkFullBackupSize(totalSize); 4506 } catch (Exception e) { 4507 Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage()); 4508 result = BackupTransport.AGENT_ERROR; 4509 } 4510 return result; 4511 } 4512 4513 @Override 4514 public void execute() { 4515 // Unused in this case 4516 } 4517 4518 @Override 4519 public void operationComplete(int result) { 4520 // got the callback, and our preflightFullBackup() method is waiting for the result 4521 if (MORE_DEBUG) { 4522 Slog.i(TAG, "Preflight op complete, result=" + result); 4523 } 4524 mResult.set(result); 4525 mLatch.countDown(); 4526 } 4527 4528 @Override 4529 public void handleTimeout() { 4530 if (MORE_DEBUG) { 4531 Slog.i(TAG, "Preflight timeout; failing"); 4532 } 4533 mResult.set(BackupTransport.AGENT_ERROR); 4534 mLatch.countDown(); 4535 } 4536 4537 @Override 4538 public long expectedSize() { 4539 try { 4540 mLatch.await(); 4541 return mResult.get(); 4542 } catch (InterruptedException e) { 4543 return BackupTransport.NO_MORE_DATA; 4544 } 4545 } 4546 } 4547 4548 class SinglePackageBackupRunner implements Runnable { 4549 final ParcelFileDescriptor mOutput; 4550 final PackageInfo mTarget; 4551 final FullBackupPreflight mPreflight; 4552 final CountDownLatch mLatch; 4553 4554 SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target, 4555 IBackupTransport transport, CountDownLatch latch) throws IOException { 4556 mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor()); 4557 mTarget = target; 4558 mPreflight = new SinglePackageBackupPreflight(transport); 4559 mLatch = latch; 4560 } 4561 4562 @Override 4563 public void run() { 4564 try { 4565 FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor()); 4566 FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName, 4567 mPreflight, false); 4568 engine.backupOnePackage(mTarget); 4569 } catch (Exception e) { 4570 Slog.e(TAG, "Exception during full package backup of " + mTarget); 4571 } finally { 4572 mLatch.countDown(); 4573 try { 4574 mOutput.close(); 4575 } catch (IOException e) { 4576 Slog.w(TAG, "Error closing transport pipe in runner"); 4577 } 4578 } 4579 } 4580 4581 long expectedSize() { 4582 return mPreflight.expectedSize(); 4583 } 4584 } 4585 } 4586 4587 // ----- Full-data backup scheduling ----- 4588 4589 /** 4590 * Schedule a job to tell us when it's a good time to run a full backup 4591 */ 4592 void scheduleNextFullBackupJob(long transportMinLatency) { 4593 synchronized (mQueueLock) { 4594 if (mFullBackupQueue.size() > 0) { 4595 // schedule the next job at the point in the future when the least-recently 4596 // backed up app comes due for backup again; or immediately if it's already 4597 // due. 4598 final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup; 4599 final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup; 4600 final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL) 4601 ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0; 4602 final long latency = Math.max(transportMinLatency, appLatency); 4603 Runnable r = new Runnable() { 4604 @Override public void run() { 4605 FullBackupJob.schedule(mContext, latency); 4606 } 4607 }; 4608 mBackupHandler.postDelayed(r, 2500); 4609 } else { 4610 if (DEBUG_SCHEDULING) { 4611 Slog.i(TAG, "Full backup queue empty; not scheduling"); 4612 } 4613 } 4614 } 4615 } 4616 4617 /** 4618 * Remove a package from the full-data queue. 4619 */ 4620 void dequeueFullBackupLocked(String packageName) { 4621 final int N = mFullBackupQueue.size(); 4622 for (int i = N-1; i >= 0; i--) { 4623 final FullBackupEntry e = mFullBackupQueue.get(i); 4624 if (packageName.equals(e.packageName)) { 4625 mFullBackupQueue.remove(i); 4626 } 4627 } 4628 } 4629 4630 /** 4631 * Enqueue full backup for the given app, with a note about when it last ran. 4632 */ 4633 void enqueueFullBackup(String packageName, long lastBackedUp) { 4634 FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp); 4635 synchronized (mQueueLock) { 4636 // First, sanity check that we aren't adding a duplicate. Slow but 4637 // straightforward; we'll have at most on the order of a few hundred 4638 // items in this list. 4639 dequeueFullBackupLocked(packageName); 4640 4641 // This is also slow but easy for modest numbers of apps: work backwards 4642 // from the end of the queue until we find an item whose last backup 4643 // time was before this one, then insert this new entry after it. If we're 4644 // adding something new we don't bother scanning, and just prepend. 4645 int which = -1; 4646 if (lastBackedUp > 0) { 4647 for (which = mFullBackupQueue.size() - 1; which >= 0; which--) { 4648 final FullBackupEntry entry = mFullBackupQueue.get(which); 4649 if (entry.lastBackup <= lastBackedUp) { 4650 mFullBackupQueue.add(which + 1, newEntry); 4651 break; 4652 } 4653 } 4654 } 4655 if (which < 0) { 4656 // this one is earlier than any existing one, so prepend 4657 mFullBackupQueue.add(0, newEntry); 4658 } 4659 } 4660 writeFullBackupScheduleAsync(); 4661 } 4662 4663 private boolean fullBackupAllowable(IBackupTransport transport) { 4664 if (transport == null) { 4665 Slog.w(TAG, "Transport not present; full data backup not performed"); 4666 return false; 4667 } 4668 4669 // Don't proceed unless we have already established package metadata 4670 // for the current dataset via a key/value backup pass. 4671 try { 4672 File stateDir = new File(mBaseStateDir, transport.transportDirName()); 4673 File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL); 4674 if (pmState.length() <= 0) { 4675 if (DEBUG) { 4676 Slog.i(TAG, "Full backup requested but dataset not yet initialized"); 4677 } 4678 return false; 4679 } 4680 } catch (Exception e) { 4681 Slog.w(TAG, "Unable to contact transport"); 4682 return false; 4683 } 4684 4685 return true; 4686 } 4687 4688 /** 4689 * Conditions are right for a full backup operation, so run one. The model we use is 4690 * to perform one app backup per scheduled job execution, and to reschedule the job 4691 * with zero latency as long as conditions remain right and we still have work to do. 4692 * 4693 * <p>This is the "start a full backup operation" entry point called by the scheduled job. 4694 * 4695 * @return Whether ongoing work will continue. The return value here will be passed 4696 * along as the return value to the scheduled job's onStartJob() callback. 4697 */ 4698 boolean beginFullBackup(FullBackupJob scheduledJob) { 4699 long now = System.currentTimeMillis(); 4700 FullBackupEntry entry = null; 4701 long latency = MIN_FULL_BACKUP_INTERVAL; 4702 4703 if (!mEnabled || !mProvisioned) { 4704 // Backups are globally disabled, so don't proceed. We also don't reschedule 4705 // the job driving automatic backups; that job will be scheduled again when 4706 // the user enables backup. 4707 if (MORE_DEBUG) { 4708 Slog.i(TAG, "beginFullBackup but e=" + mEnabled 4709 + " p=" + mProvisioned + "; ignoring"); 4710 } 4711 return false; 4712 } 4713 4714 // Don't run the backup if we're in battery saver mode, but reschedule 4715 // to try again in the not-so-distant future. 4716 if (mPowerManager.isPowerSaveMode()) { 4717 if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode"); 4718 FullBackupJob.schedule(mContext, KeyValueBackupJob.BATCH_INTERVAL); 4719 return false; 4720 } 4721 4722 if (DEBUG_SCHEDULING) { 4723 Slog.i(TAG, "Beginning scheduled full backup operation"); 4724 } 4725 4726 // Great; we're able to run full backup jobs now. See if we have any work to do. 4727 synchronized (mQueueLock) { 4728 if (mRunningFullBackupTask != null) { 4729 Slog.e(TAG, "Backup triggered but one already/still running!"); 4730 return false; 4731 } 4732 4733 // At this point we think that we have work to do, but possibly not right now. 4734 // Any exit without actually running backups will also require that we 4735 // reschedule the job. 4736 boolean runBackup = true; 4737 boolean headBusy; 4738 4739 do { 4740 // Recheck each time, because culling due to ineligibility may 4741 // have emptied the queue. 4742 if (mFullBackupQueue.size() == 0) { 4743 // no work to do so just bow out 4744 if (DEBUG) { 4745 Slog.i(TAG, "Backup queue empty; doing nothing"); 4746 } 4747 runBackup = false; 4748 break; 4749 } 4750 4751 headBusy = false; 4752 4753 if (!fullBackupAllowable(getTransport(mCurrentTransport))) { 4754 if (MORE_DEBUG) { 4755 Slog.i(TAG, "Preconditions not met; not running full backup"); 4756 } 4757 runBackup = false; 4758 // Typically this means we haven't run a key/value backup yet. Back off 4759 // full-backup operations by the key/value job's run interval so that 4760 // next time we run, we are likely to be able to make progress. 4761 latency = KeyValueBackupJob.BATCH_INTERVAL; 4762 } 4763 4764 if (runBackup) { 4765 entry = mFullBackupQueue.get(0); 4766 long timeSinceRun = now - entry.lastBackup; 4767 runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL); 4768 if (!runBackup) { 4769 // It's too early to back up the next thing in the queue, so bow out 4770 if (MORE_DEBUG) { 4771 Slog.i(TAG, "Device ready but too early to back up next app"); 4772 } 4773 // Wait until the next app in the queue falls due for a full data backup 4774 latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun; 4775 break; // we know we aren't doing work yet, so bail. 4776 } 4777 4778 try { 4779 PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0); 4780 if (!appGetsFullBackup(appInfo)) { 4781 // The head app isn't supposed to get full-data backups [any more]; 4782 // so we cull it and force a loop around to consider the new head 4783 // app. 4784 if (MORE_DEBUG) { 4785 Slog.i(TAG, "Culling package " + entry.packageName 4786 + " in full-backup queue but not eligible"); 4787 } 4788 mFullBackupQueue.remove(0); 4789 headBusy = true; // force the while() condition 4790 continue; 4791 } 4792 4793 headBusy = mActivityManager.isAppForeground(appInfo.applicationInfo.uid); 4794 4795 if (headBusy) { 4796 final long nextEligible = System.currentTimeMillis() 4797 + BUSY_BACKOFF_MIN_MILLIS 4798 + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ); 4799 if (DEBUG_SCHEDULING) { 4800 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 4801 Slog.i(TAG, "Full backup time but " + entry.packageName 4802 + " is busy; deferring to " 4803 + sdf.format(new Date(nextEligible))); 4804 } 4805 // This relocates the app's entry from the head of the queue to 4806 // its order-appropriate position further down, so upon looping 4807 // a new candidate will be considered at the head. 4808 enqueueFullBackup(entry.packageName, 4809 nextEligible - MIN_FULL_BACKUP_INTERVAL); 4810 } 4811 } catch (NameNotFoundException nnf) { 4812 // So, we think we want to back this up, but it turns out the package 4813 // in question is no longer installed. We want to drop it from the 4814 // queue entirely and move on, but if there's nothing else in the queue 4815 // we should bail entirely. headBusy cannot have been set to true yet. 4816 runBackup = (mFullBackupQueue.size() > 1); 4817 } catch (RemoteException e) { 4818 // Cannot happen; the Activity Manager is in the same process 4819 } 4820 } 4821 } while (headBusy); 4822 4823 if (!runBackup) { 4824 if (DEBUG_SCHEDULING) { 4825 Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency); 4826 } 4827 final long deferTime = latency; // pin for the closure 4828 mBackupHandler.post(new Runnable() { 4829 @Override public void run() { 4830 FullBackupJob.schedule(mContext, deferTime); 4831 } 4832 }); 4833 return false; 4834 } 4835 4836 // Okay, the top thing is ready for backup now. Do it. 4837 mFullBackupQueue.remove(0); 4838 CountDownLatch latch = new CountDownLatch(1); 4839 String[] pkg = new String[] {entry.packageName}; 4840 mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true, 4841 scheduledJob, latch, null, false /* userInitiated */); 4842 // Acquiring wakelock for PerformFullTransportBackupTask before its start. 4843 mWakelock.acquire(); 4844 (new Thread(mRunningFullBackupTask)).start(); 4845 } 4846 4847 return true; 4848 } 4849 4850 // The job scheduler says our constraints don't hold any more, 4851 // so tear down any ongoing backup task right away. 4852 void endFullBackup() { 4853 synchronized (mQueueLock) { 4854 if (mRunningFullBackupTask != null) { 4855 if (DEBUG_SCHEDULING) { 4856 Slog.i(TAG, "Telling running backup to stop"); 4857 } 4858 mRunningFullBackupTask.setRunning(false); 4859 } 4860 } 4861 } 4862 4863 // ----- Restore infrastructure ----- 4864 4865 abstract class RestoreEngine { 4866 static final String TAG = "RestoreEngine"; 4867 4868 public static final int SUCCESS = 0; 4869 public static final int TARGET_FAILURE = -2; 4870 public static final int TRANSPORT_FAILURE = -3; 4871 4872 private AtomicBoolean mRunning = new AtomicBoolean(false); 4873 private AtomicInteger mResult = new AtomicInteger(SUCCESS); 4874 4875 public boolean isRunning() { 4876 return mRunning.get(); 4877 } 4878 4879 public void setRunning(boolean stillRunning) { 4880 synchronized (mRunning) { 4881 mRunning.set(stillRunning); 4882 mRunning.notifyAll(); 4883 } 4884 } 4885 4886 public int waitForResult() { 4887 synchronized (mRunning) { 4888 while (isRunning()) { 4889 try { 4890 mRunning.wait(); 4891 } catch (InterruptedException e) {} 4892 } 4893 } 4894 return getResult(); 4895 } 4896 4897 public int getResult() { 4898 return mResult.get(); 4899 } 4900 4901 public void setResult(int result) { 4902 mResult.set(result); 4903 } 4904 4905 // TODO: abstract restore state and APIs 4906 } 4907 4908 // ----- Full restore from a file/socket ----- 4909 4910 // Description of a file in the restore datastream 4911 static class FileMetadata { 4912 String packageName; // name of the owning app 4913 String installerPackageName; // name of the market-type app that installed the owner 4914 int type; // e.g. BackupAgent.TYPE_DIRECTORY 4915 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN 4916 String path; // subpath within the semantic domain 4917 long mode; // e.g. 0666 (actually int) 4918 long mtime; // last mod time, UTC time_t (actually int) 4919 long size; // bytes of content 4920 4921 @Override 4922 public String toString() { 4923 StringBuilder sb = new StringBuilder(128); 4924 sb.append("FileMetadata{"); 4925 sb.append(packageName); sb.append(','); 4926 sb.append(type); sb.append(','); 4927 sb.append(domain); sb.append(':'); sb.append(path); sb.append(','); 4928 sb.append(size); 4929 sb.append('}'); 4930 return sb.toString(); 4931 } 4932 } 4933 4934 enum RestorePolicy { 4935 IGNORE, 4936 ACCEPT, 4937 ACCEPT_IF_APK 4938 } 4939 4940 // Full restore engine, used by both adb restore and transport-based full restore 4941 class FullRestoreEngine extends RestoreEngine { 4942 // Dedicated observer, if any 4943 IFullBackupRestoreObserver mObserver; 4944 4945 // Where we're delivering the file data as we go 4946 IBackupAgent mAgent; 4947 4948 // Are we permitted to only deliver a specific package's metadata? 4949 PackageInfo mOnlyPackage; 4950 4951 boolean mAllowApks; 4952 boolean mAllowObbs; 4953 4954 // Which package are we currently handling data for? 4955 String mAgentPackage; 4956 4957 // Info for working with the target app process 4958 ApplicationInfo mTargetApp; 4959 4960 // Machinery for restoring OBBs 4961 FullBackupObbConnection mObbConnection = null; 4962 4963 // possible handling states for a given package in the restore dataset 4964 final HashMap<String, RestorePolicy> mPackagePolicies 4965 = new HashMap<String, RestorePolicy>(); 4966 4967 // installer package names for each encountered app, derived from the manifests 4968 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 4969 4970 // Signatures for a given package found in its manifest file 4971 final HashMap<String, Signature[]> mManifestSignatures 4972 = new HashMap<String, Signature[]>(); 4973 4974 // Packages we've already wiped data on when restoring their first file 4975 final HashSet<String> mClearedPackages = new HashSet<String>(); 4976 4977 // How much data have we moved? 4978 long mBytes; 4979 4980 // Working buffer 4981 byte[] mBuffer; 4982 4983 // Pipes for moving data 4984 ParcelFileDescriptor[] mPipes = null; 4985 4986 // Widget blob to be restored out-of-band 4987 byte[] mWidgetData = null; 4988 4989 // Runner that can be placed in a separate thread to do in-process 4990 // invocations of the full restore API asynchronously 4991 class RestoreFileRunnable implements Runnable { 4992 IBackupAgent mAgent; 4993 FileMetadata mInfo; 4994 ParcelFileDescriptor mSocket; 4995 int mToken; 4996 4997 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 4998 ParcelFileDescriptor socket, int token) throws IOException { 4999 mAgent = agent; 5000 mInfo = info; 5001 mToken = token; 5002 5003 // This class is used strictly for process-local binder invocations. The 5004 // semantics of ParcelFileDescriptor differ in this case; in particular, we 5005 // do not automatically get a 'dup'ed descriptor that we can can continue 5006 // to use asynchronously from the caller. So, we make sure to dup it ourselves 5007 // before proceeding to do the restore. 5008 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 5009 } 5010 5011 @Override 5012 public void run() { 5013 try { 5014 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 5015 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 5016 mToken, mBackupManagerBinder); 5017 } catch (RemoteException e) { 5018 // never happens; this is used strictly for local binder calls 5019 } 5020 } 5021 } 5022 5023 public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage, 5024 boolean allowApks, boolean allowObbs) { 5025 mObserver = observer; 5026 mOnlyPackage = onlyPackage; 5027 mAllowApks = allowApks; 5028 mAllowObbs = allowObbs; 5029 mBuffer = new byte[32 * 1024]; 5030 mBytes = 0; 5031 } 5032 5033 public IBackupAgent getAgent() { 5034 return mAgent; 5035 } 5036 5037 public boolean restoreOneFile(InputStream instream, boolean mustKillAgent) { 5038 if (!isRunning()) { 5039 Slog.w(TAG, "Restore engine used after halting"); 5040 return false; 5041 } 5042 5043 FileMetadata info; 5044 try { 5045 if (MORE_DEBUG) { 5046 Slog.v(TAG, "Reading tar header for restoring file"); 5047 } 5048 info = readTarHeaders(instream); 5049 if (info != null) { 5050 if (MORE_DEBUG) { 5051 dumpFileMetadata(info); 5052 } 5053 5054 final String pkg = info.packageName; 5055 if (!pkg.equals(mAgentPackage)) { 5056 // In the single-package case, it's a semantic error to expect 5057 // one app's data but see a different app's on the wire 5058 if (mOnlyPackage != null) { 5059 if (!pkg.equals(mOnlyPackage.packageName)) { 5060 Slog.w(TAG, "Expected data for " + mOnlyPackage 5061 + " but saw " + pkg); 5062 setResult(RestoreEngine.TRANSPORT_FAILURE); 5063 setRunning(false); 5064 return false; 5065 } 5066 } 5067 5068 // okay, change in package; set up our various 5069 // bookkeeping if we haven't seen it yet 5070 if (!mPackagePolicies.containsKey(pkg)) { 5071 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5072 } 5073 5074 // Clean up the previous agent relationship if necessary, 5075 // and let the observer know we're considering a new app. 5076 if (mAgent != null) { 5077 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 5078 // Now we're really done 5079 tearDownPipes(); 5080 tearDownAgent(mTargetApp); 5081 mTargetApp = null; 5082 mAgentPackage = null; 5083 } 5084 } 5085 5086 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 5087 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 5088 mPackageInstallers.put(pkg, info.installerPackageName); 5089 // We've read only the manifest content itself at this point, 5090 // so consume the footer before looping around to the next 5091 // input file 5092 skipTarPadding(info.size, instream); 5093 sendOnRestorePackage(pkg); 5094 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 5095 // Metadata blobs! 5096 readMetadata(info, instream); 5097 skipTarPadding(info.size, instream); 5098 } else { 5099 // Non-manifest, so it's actual file data. Is this a package 5100 // we're ignoring? 5101 boolean okay = true; 5102 RestorePolicy policy = mPackagePolicies.get(pkg); 5103 switch (policy) { 5104 case IGNORE: 5105 okay = false; 5106 break; 5107 5108 case ACCEPT_IF_APK: 5109 // If we're in accept-if-apk state, then the first file we 5110 // see MUST be the apk. 5111 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5112 if (DEBUG) Slog.d(TAG, "APK file; installing"); 5113 // Try to install the app. 5114 String installerName = mPackageInstallers.get(pkg); 5115 okay = installApk(info, installerName, instream); 5116 // good to go; promote to ACCEPT 5117 mPackagePolicies.put(pkg, (okay) 5118 ? RestorePolicy.ACCEPT 5119 : RestorePolicy.IGNORE); 5120 // At this point we've consumed this file entry 5121 // ourselves, so just strip the tar footer and 5122 // go on to the next file in the input stream 5123 skipTarPadding(info.size, instream); 5124 return true; 5125 } else { 5126 // File data before (or without) the apk. We can't 5127 // handle it coherently in this case so ignore it. 5128 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5129 okay = false; 5130 } 5131 break; 5132 5133 case ACCEPT: 5134 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 5135 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 5136 // we can take the data without the apk, so we 5137 // *want* to do so. skip the apk by declaring this 5138 // one file not-okay without changing the restore 5139 // policy for the package. 5140 okay = false; 5141 } 5142 break; 5143 5144 default: 5145 // Something has gone dreadfully wrong when determining 5146 // the restore policy from the manifest. Ignore the 5147 // rest of this package's data. 5148 Slog.e(TAG, "Invalid policy from manifest"); 5149 okay = false; 5150 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5151 break; 5152 } 5153 5154 // Is it a *file* we need to drop? 5155 if (!isRestorableFile(info)) { 5156 okay = false; 5157 } 5158 5159 // If the policy is satisfied, go ahead and set up to pipe the 5160 // data to the agent. 5161 if (DEBUG && okay && mAgent != null) { 5162 Slog.i(TAG, "Reusing existing agent instance"); 5163 } 5164 if (okay && mAgent == null) { 5165 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 5166 5167 try { 5168 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 5169 5170 // If we haven't sent any data to this app yet, we probably 5171 // need to clear it first. Check that. 5172 if (!mClearedPackages.contains(pkg)) { 5173 // apps with their own backup agents are 5174 // responsible for coherently managing a full 5175 // restore. 5176 if (mTargetApp.backupAgentName == null) { 5177 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 5178 clearApplicationDataSynchronous(pkg); 5179 } else { 5180 if (MORE_DEBUG) Slog.d(TAG, "backup agent (" 5181 + mTargetApp.backupAgentName + ") => no clear"); 5182 } 5183 mClearedPackages.add(pkg); 5184 } else { 5185 if (MORE_DEBUG) { 5186 Slog.d(TAG, "We've initialized this app already; no clear required"); 5187 } 5188 } 5189 5190 // All set; now set up the IPC and launch the agent 5191 setUpPipes(); 5192 mAgent = bindToAgentSynchronous(mTargetApp, 5193 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 5194 mAgentPackage = pkg; 5195 } catch (IOException e) { 5196 // fall through to error handling 5197 } catch (NameNotFoundException e) { 5198 // fall through to error handling 5199 } 5200 5201 if (mAgent == null) { 5202 Slog.e(TAG, "Unable to create agent for " + pkg); 5203 okay = false; 5204 tearDownPipes(); 5205 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5206 } 5207 } 5208 5209 // Sanity check: make sure we never give data to the wrong app. This 5210 // should never happen but a little paranoia here won't go amiss. 5211 if (okay && !pkg.equals(mAgentPackage)) { 5212 Slog.e(TAG, "Restoring data for " + pkg 5213 + " but agent is for " + mAgentPackage); 5214 okay = false; 5215 } 5216 5217 // At this point we have an agent ready to handle the full 5218 // restore data as well as a pipe for sending data to 5219 // that agent. Tell the agent to start reading from the 5220 // pipe. 5221 if (okay) { 5222 boolean agentSuccess = true; 5223 long toCopy = info.size; 5224 final int token = generateToken(); 5225 try { 5226 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 5227 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 5228 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 5229 + " : " + info.path); 5230 mObbConnection.restoreObbFile(pkg, mPipes[0], 5231 info.size, info.type, info.path, info.mode, 5232 info.mtime, token, mBackupManagerBinder); 5233 } else { 5234 if (MORE_DEBUG) Slog.d(TAG, "Invoking agent to restore file " 5235 + info.path); 5236 // fire up the app's agent listening on the socket. If 5237 // the agent is running in the system process we can't 5238 // just invoke it asynchronously, so we provide a thread 5239 // for it here. 5240 if (mTargetApp.processName.equals("system")) { 5241 Slog.d(TAG, "system process agent - spinning a thread"); 5242 RestoreFileRunnable runner = new RestoreFileRunnable( 5243 mAgent, info, mPipes[0], token); 5244 new Thread(runner, "restore-sys-runner").start(); 5245 } else { 5246 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 5247 info.domain, info.path, info.mode, info.mtime, 5248 token, mBackupManagerBinder); 5249 } 5250 } 5251 } catch (IOException e) { 5252 // couldn't dup the socket for a process-local restore 5253 Slog.d(TAG, "Couldn't establish restore"); 5254 agentSuccess = false; 5255 okay = false; 5256 } catch (RemoteException e) { 5257 // whoops, remote entity went away. We'll eat the content 5258 // ourselves, then, and not copy it over. 5259 Slog.e(TAG, "Agent crashed during full restore"); 5260 agentSuccess = false; 5261 okay = false; 5262 } 5263 5264 // Copy over the data if the agent is still good 5265 if (okay) { 5266 if (MORE_DEBUG) { 5267 Slog.v(TAG, " copying to restore agent: " 5268 + toCopy + " bytes"); 5269 } 5270 boolean pipeOkay = true; 5271 FileOutputStream pipe = new FileOutputStream( 5272 mPipes[1].getFileDescriptor()); 5273 while (toCopy > 0) { 5274 int toRead = (toCopy > mBuffer.length) 5275 ? mBuffer.length : (int)toCopy; 5276 int nRead = instream.read(mBuffer, 0, toRead); 5277 if (nRead >= 0) mBytes += nRead; 5278 if (nRead <= 0) break; 5279 toCopy -= nRead; 5280 5281 // send it to the output pipe as long as things 5282 // are still good 5283 if (pipeOkay) { 5284 try { 5285 pipe.write(mBuffer, 0, nRead); 5286 } catch (IOException e) { 5287 Slog.e(TAG, "Failed to write to restore pipe", e); 5288 pipeOkay = false; 5289 } 5290 } 5291 } 5292 5293 // done sending that file! Now we just need to consume 5294 // the delta from info.size to the end of block. 5295 skipTarPadding(info.size, instream); 5296 5297 // and now that we've sent it all, wait for the remote 5298 // side to acknowledge receipt 5299 agentSuccess = waitUntilOperationComplete(token); 5300 } 5301 5302 // okay, if the remote end failed at any point, deal with 5303 // it by ignoring the rest of the restore on it 5304 if (!agentSuccess) { 5305 Slog.w(TAG, "Agent failure; ending restore"); 5306 mBackupHandler.removeMessages(MSG_TIMEOUT); 5307 tearDownPipes(); 5308 tearDownAgent(mTargetApp); 5309 mAgent = null; 5310 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 5311 5312 // If this was a single-package restore, we halt immediately 5313 // with an agent error under these circumstances 5314 if (mOnlyPackage != null) { 5315 setResult(RestoreEngine.TARGET_FAILURE); 5316 setRunning(false); 5317 return false; 5318 } 5319 } 5320 } 5321 5322 // Problems setting up the agent communication, an explicitly 5323 // dropped file, or an already-ignored package: skip to the 5324 // next stream entry by reading and discarding this file. 5325 if (!okay) { 5326 if (MORE_DEBUG) Slog.d(TAG, "[discarding file content]"); 5327 long bytesToConsume = (info.size + 511) & ~511; 5328 while (bytesToConsume > 0) { 5329 int toRead = (bytesToConsume > mBuffer.length) 5330 ? mBuffer.length : (int)bytesToConsume; 5331 long nRead = instream.read(mBuffer, 0, toRead); 5332 if (nRead >= 0) mBytes += nRead; 5333 if (nRead <= 0) break; 5334 bytesToConsume -= nRead; 5335 } 5336 } 5337 } 5338 } 5339 } catch (IOException e) { 5340 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 5341 setResult(RestoreEngine.TRANSPORT_FAILURE); 5342 info = null; 5343 } 5344 5345 // If we got here we're either running smoothly or we've finished 5346 if (info == null) { 5347 if (MORE_DEBUG) { 5348 Slog.i(TAG, "No [more] data for this package; tearing down"); 5349 } 5350 tearDownPipes(); 5351 setRunning(false); 5352 if (mustKillAgent) { 5353 tearDownAgent(mTargetApp); 5354 } 5355 } 5356 return (info != null); 5357 } 5358 5359 void setUpPipes() throws IOException { 5360 mPipes = ParcelFileDescriptor.createPipe(); 5361 } 5362 5363 void tearDownPipes() { 5364 if (mPipes != null) { 5365 try { 5366 mPipes[0].close(); 5367 mPipes[0] = null; 5368 mPipes[1].close(); 5369 mPipes[1] = null; 5370 } catch (IOException e) { 5371 Slog.w(TAG, "Couldn't close agent pipes", e); 5372 } 5373 mPipes = null; 5374 } 5375 } 5376 5377 void tearDownAgent(ApplicationInfo app) { 5378 if (mAgent != null) { 5379 tearDownAgentAndKill(app); 5380 mAgent = null; 5381 } 5382 } 5383 5384 class RestoreInstallObserver extends PackageInstallObserver { 5385 final AtomicBoolean mDone = new AtomicBoolean(); 5386 String mPackageName; 5387 int mResult; 5388 5389 public void reset() { 5390 synchronized (mDone) { 5391 mDone.set(false); 5392 } 5393 } 5394 5395 public void waitForCompletion() { 5396 synchronized (mDone) { 5397 while (mDone.get() == false) { 5398 try { 5399 mDone.wait(); 5400 } catch (InterruptedException e) { } 5401 } 5402 } 5403 } 5404 5405 int getResult() { 5406 return mResult; 5407 } 5408 5409 @Override 5410 public void onPackageInstalled(String packageName, int returnCode, 5411 String msg, Bundle extras) { 5412 synchronized (mDone) { 5413 mResult = returnCode; 5414 mPackageName = packageName; 5415 mDone.set(true); 5416 mDone.notifyAll(); 5417 } 5418 } 5419 } 5420 5421 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 5422 final AtomicBoolean mDone = new AtomicBoolean(); 5423 int mResult; 5424 5425 public void reset() { 5426 synchronized (mDone) { 5427 mDone.set(false); 5428 } 5429 } 5430 5431 public void waitForCompletion() { 5432 synchronized (mDone) { 5433 while (mDone.get() == false) { 5434 try { 5435 mDone.wait(); 5436 } catch (InterruptedException e) { } 5437 } 5438 } 5439 } 5440 5441 @Override 5442 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 5443 synchronized (mDone) { 5444 mResult = returnCode; 5445 mDone.set(true); 5446 mDone.notifyAll(); 5447 } 5448 } 5449 } 5450 5451 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 5452 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 5453 5454 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 5455 boolean okay = true; 5456 5457 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 5458 5459 // The file content is an .apk file. Copy it out to a staging location and 5460 // attempt to install it. 5461 File apkFile = new File(mDataDir, info.packageName); 5462 try { 5463 FileOutputStream apkStream = new FileOutputStream(apkFile); 5464 byte[] buffer = new byte[32 * 1024]; 5465 long size = info.size; 5466 while (size > 0) { 5467 long toRead = (buffer.length < size) ? buffer.length : size; 5468 int didRead = instream.read(buffer, 0, (int)toRead); 5469 if (didRead >= 0) mBytes += didRead; 5470 apkStream.write(buffer, 0, didRead); 5471 size -= didRead; 5472 } 5473 apkStream.close(); 5474 5475 // make sure the installer can read it 5476 apkFile.setReadable(true, false); 5477 5478 // Now install it 5479 Uri packageUri = Uri.fromFile(apkFile); 5480 mInstallObserver.reset(); 5481 mPackageManager.installPackage(packageUri, mInstallObserver, 5482 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 5483 installerPackage); 5484 mInstallObserver.waitForCompletion(); 5485 5486 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 5487 // The only time we continue to accept install of data even if the 5488 // apk install failed is if we had already determined that we could 5489 // accept the data regardless. 5490 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 5491 okay = false; 5492 } 5493 } else { 5494 // Okay, the install succeeded. Make sure it was the right app. 5495 boolean uninstall = false; 5496 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 5497 Slog.w(TAG, "Restore stream claimed to include apk for " 5498 + info.packageName + " but apk was really " 5499 + mInstallObserver.mPackageName); 5500 // delete the package we just put in place; it might be fraudulent 5501 okay = false; 5502 uninstall = true; 5503 } else { 5504 try { 5505 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 5506 PackageManager.GET_SIGNATURES); 5507 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 5508 Slog.w(TAG, "Restore stream contains apk of package " 5509 + info.packageName + " but it disallows backup/restore"); 5510 okay = false; 5511 } else { 5512 // So far so good -- do the signatures match the manifest? 5513 Signature[] sigs = mManifestSignatures.get(info.packageName); 5514 if (signaturesMatch(sigs, pkg)) { 5515 // If this is a system-uid app without a declared backup agent, 5516 // don't restore any of the file data. 5517 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 5518 && (pkg.applicationInfo.backupAgentName == null)) { 5519 Slog.w(TAG, "Installed app " + info.packageName 5520 + " has restricted uid and no agent"); 5521 okay = false; 5522 } 5523 } else { 5524 Slog.w(TAG, "Installed app " + info.packageName 5525 + " signatures do not match restore manifest"); 5526 okay = false; 5527 uninstall = true; 5528 } 5529 } 5530 } catch (NameNotFoundException e) { 5531 Slog.w(TAG, "Install of package " + info.packageName 5532 + " succeeded but now not found"); 5533 okay = false; 5534 } 5535 } 5536 5537 // If we're not okay at this point, we need to delete the package 5538 // that we just installed. 5539 if (uninstall) { 5540 mDeleteObserver.reset(); 5541 mPackageManager.deletePackage(mInstallObserver.mPackageName, 5542 mDeleteObserver, 0); 5543 mDeleteObserver.waitForCompletion(); 5544 } 5545 } 5546 } catch (IOException e) { 5547 Slog.e(TAG, "Unable to transcribe restored apk for install"); 5548 okay = false; 5549 } finally { 5550 apkFile.delete(); 5551 } 5552 5553 return okay; 5554 } 5555 5556 // Given an actual file content size, consume the post-content padding mandated 5557 // by the tar format. 5558 void skipTarPadding(long size, InputStream instream) throws IOException { 5559 long partial = (size + 512) % 512; 5560 if (partial > 0) { 5561 final int needed = 512 - (int)partial; 5562 if (MORE_DEBUG) { 5563 Slog.i(TAG, "Skipping tar padding: " + needed + " bytes"); 5564 } 5565 byte[] buffer = new byte[needed]; 5566 if (readExactly(instream, buffer, 0, needed) == needed) { 5567 mBytes += needed; 5568 } else throw new IOException("Unexpected EOF in padding"); 5569 } 5570 } 5571 5572 // Read a widget metadata file, returning the restored blob 5573 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 5574 // Fail on suspiciously large widget dump files 5575 if (info.size > 64 * 1024) { 5576 throw new IOException("Metadata too big; corrupt? size=" + info.size); 5577 } 5578 5579 byte[] buffer = new byte[(int) info.size]; 5580 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5581 mBytes += info.size; 5582 } else throw new IOException("Unexpected EOF in widget data"); 5583 5584 String[] str = new String[1]; 5585 int offset = extractLine(buffer, 0, str); 5586 int version = Integer.parseInt(str[0]); 5587 if (version == BACKUP_MANIFEST_VERSION) { 5588 offset = extractLine(buffer, offset, str); 5589 final String pkg = str[0]; 5590 if (info.packageName.equals(pkg)) { 5591 // Data checks out -- the rest of the buffer is a concatenation of 5592 // binary blobs as described in the comment at writeAppWidgetData() 5593 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 5594 offset, buffer.length - offset); 5595 DataInputStream in = new DataInputStream(bin); 5596 while (bin.available() > 0) { 5597 int token = in.readInt(); 5598 int size = in.readInt(); 5599 if (size > 64 * 1024) { 5600 throw new IOException("Datum " 5601 + Integer.toHexString(token) 5602 + " too big; corrupt? size=" + info.size); 5603 } 5604 switch (token) { 5605 case BACKUP_WIDGET_METADATA_TOKEN: 5606 { 5607 if (MORE_DEBUG) { 5608 Slog.i(TAG, "Got widget metadata for " + info.packageName); 5609 } 5610 mWidgetData = new byte[size]; 5611 in.read(mWidgetData); 5612 break; 5613 } 5614 default: 5615 { 5616 if (DEBUG) { 5617 Slog.i(TAG, "Ignoring metadata blob " 5618 + Integer.toHexString(token) 5619 + " for " + info.packageName); 5620 } 5621 in.skipBytes(size); 5622 break; 5623 } 5624 } 5625 } 5626 } else { 5627 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 5628 + " but widget data for " + pkg); 5629 } 5630 } else { 5631 Slog.w(TAG, "Unsupported metadata version " + version); 5632 } 5633 } 5634 5635 // Returns a policy constant 5636 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 5637 throws IOException { 5638 // Fail on suspiciously large manifest files 5639 if (info.size > 64 * 1024) { 5640 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 5641 } 5642 5643 byte[] buffer = new byte[(int) info.size]; 5644 if (MORE_DEBUG) { 5645 Slog.i(TAG, " readAppManifest() looking for " + info.size + " bytes, " 5646 + mBytes + " already consumed"); 5647 } 5648 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 5649 mBytes += info.size; 5650 } else throw new IOException("Unexpected EOF in manifest"); 5651 5652 RestorePolicy policy = RestorePolicy.IGNORE; 5653 String[] str = new String[1]; 5654 int offset = 0; 5655 5656 try { 5657 offset = extractLine(buffer, offset, str); 5658 int version = Integer.parseInt(str[0]); 5659 if (version == BACKUP_MANIFEST_VERSION) { 5660 offset = extractLine(buffer, offset, str); 5661 String manifestPackage = str[0]; 5662 // TODO: handle <original-package> 5663 if (manifestPackage.equals(info.packageName)) { 5664 offset = extractLine(buffer, offset, str); 5665 version = Integer.parseInt(str[0]); // app version 5666 offset = extractLine(buffer, offset, str); 5667 // This is the platform version, which we don't use, but we parse it 5668 // as a safety against corruption in the manifest. 5669 Integer.parseInt(str[0]); 5670 offset = extractLine(buffer, offset, str); 5671 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 5672 offset = extractLine(buffer, offset, str); 5673 boolean hasApk = str[0].equals("1"); 5674 offset = extractLine(buffer, offset, str); 5675 int numSigs = Integer.parseInt(str[0]); 5676 if (numSigs > 0) { 5677 Signature[] sigs = new Signature[numSigs]; 5678 for (int i = 0; i < numSigs; i++) { 5679 offset = extractLine(buffer, offset, str); 5680 sigs[i] = new Signature(str[0]); 5681 } 5682 mManifestSignatures.put(info.packageName, sigs); 5683 5684 // Okay, got the manifest info we need... 5685 try { 5686 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 5687 info.packageName, PackageManager.GET_SIGNATURES); 5688 // Fall through to IGNORE if the app explicitly disallows backup 5689 final int flags = pkgInfo.applicationInfo.flags; 5690 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 5691 // Restore system-uid-space packages only if they have 5692 // defined a custom backup agent 5693 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 5694 || (pkgInfo.applicationInfo.backupAgentName != null)) { 5695 // Verify signatures against any installed version; if they 5696 // don't match, then we fall though and ignore the data. The 5697 // signatureMatch() method explicitly ignores the signature 5698 // check for packages installed on the system partition, because 5699 // such packages are signed with the platform cert instead of 5700 // the app developer's cert, so they're different on every 5701 // device. 5702 if (signaturesMatch(sigs, pkgInfo)) { 5703 if (pkgInfo.versionCode >= version) { 5704 Slog.i(TAG, "Sig + version match; taking data"); 5705 policy = RestorePolicy.ACCEPT; 5706 } else { 5707 // The data is from a newer version of the app than 5708 // is presently installed. That means we can only 5709 // use it if the matching apk is also supplied. 5710 if (mAllowApks) { 5711 Slog.i(TAG, "Data version " + version 5712 + " is newer than installed version " 5713 + pkgInfo.versionCode 5714 + " - requiring apk"); 5715 policy = RestorePolicy.ACCEPT_IF_APK; 5716 } else { 5717 Slog.i(TAG, "Data requires newer version " 5718 + version + "; ignoring"); 5719 policy = RestorePolicy.IGNORE; 5720 } 5721 } 5722 } else { 5723 Slog.w(TAG, "Restore manifest signatures do not match " 5724 + "installed application for " + info.packageName); 5725 } 5726 } else { 5727 Slog.w(TAG, "Package " + info.packageName 5728 + " is system level with no agent"); 5729 } 5730 } else { 5731 if (DEBUG) Slog.i(TAG, "Restore manifest from " 5732 + info.packageName + " but allowBackup=false"); 5733 } 5734 } catch (NameNotFoundException e) { 5735 // Okay, the target app isn't installed. We can process 5736 // the restore properly only if the dataset provides the 5737 // apk file and we can successfully install it. 5738 if (mAllowApks) { 5739 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 5740 + " not installed; requiring apk in dataset"); 5741 policy = RestorePolicy.ACCEPT_IF_APK; 5742 } else { 5743 policy = RestorePolicy.IGNORE; 5744 } 5745 } 5746 5747 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 5748 Slog.i(TAG, "Cannot restore package " + info.packageName 5749 + " without the matching .apk"); 5750 } 5751 } else { 5752 Slog.i(TAG, "Missing signature on backed-up package " 5753 + info.packageName); 5754 } 5755 } else { 5756 Slog.i(TAG, "Expected package " + info.packageName 5757 + " but restore manifest claims " + manifestPackage); 5758 } 5759 } else { 5760 Slog.i(TAG, "Unknown restore manifest version " + version 5761 + " for package " + info.packageName); 5762 } 5763 } catch (NumberFormatException e) { 5764 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 5765 } catch (IllegalArgumentException e) { 5766 Slog.w(TAG, e.getMessage()); 5767 } 5768 5769 return policy; 5770 } 5771 5772 // Builds a line from a byte buffer starting at 'offset', and returns 5773 // the index of the next unconsumed data in the buffer. 5774 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 5775 final int end = buffer.length; 5776 if (offset >= end) throw new IOException("Incomplete data"); 5777 5778 int pos; 5779 for (pos = offset; pos < end; pos++) { 5780 byte c = buffer[pos]; 5781 // at LF we declare end of line, and return the next char as the 5782 // starting point for the next time through 5783 if (c == '\n') { 5784 break; 5785 } 5786 } 5787 outStr[0] = new String(buffer, offset, pos - offset); 5788 pos++; // may be pointing an extra byte past the end but that's okay 5789 return pos; 5790 } 5791 5792 void dumpFileMetadata(FileMetadata info) { 5793 if (MORE_DEBUG) { 5794 StringBuilder b = new StringBuilder(128); 5795 5796 // mode string 5797 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 5798 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 5799 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 5800 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 5801 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 5802 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 5803 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 5804 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 5805 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 5806 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 5807 b.append(String.format(" %9d ", info.size)); 5808 5809 Date stamp = new Date(info.mtime); 5810 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 5811 5812 b.append(info.packageName); 5813 b.append(" :: "); 5814 b.append(info.domain); 5815 b.append(" :: "); 5816 b.append(info.path); 5817 5818 Slog.i(TAG, b.toString()); 5819 } 5820 } 5821 5822 // Consume a tar file header block [sequence] and accumulate the relevant metadata 5823 FileMetadata readTarHeaders(InputStream instream) throws IOException { 5824 byte[] block = new byte[512]; 5825 FileMetadata info = null; 5826 5827 boolean gotHeader = readTarHeader(instream, block); 5828 if (gotHeader) { 5829 try { 5830 // okay, presume we're okay, and extract the various metadata 5831 info = new FileMetadata(); 5832 info.size = extractRadix(block, 124, 12, 8); 5833 info.mtime = extractRadix(block, 136, 12, 8); 5834 info.mode = extractRadix(block, 100, 8, 8); 5835 5836 info.path = extractString(block, 345, 155); // prefix 5837 String path = extractString(block, 0, 100); 5838 if (path.length() > 0) { 5839 if (info.path.length() > 0) info.path += '/'; 5840 info.path += path; 5841 } 5842 5843 // tar link indicator field: 1 byte at offset 156 in the header. 5844 int typeChar = block[156]; 5845 if (typeChar == 'x') { 5846 // pax extended header, so we need to read that 5847 gotHeader = readPaxExtendedHeader(instream, info); 5848 if (gotHeader) { 5849 // and after a pax extended header comes another real header -- read 5850 // that to find the real file type 5851 gotHeader = readTarHeader(instream, block); 5852 } 5853 if (!gotHeader) throw new IOException("Bad or missing pax header"); 5854 5855 typeChar = block[156]; 5856 } 5857 5858 switch (typeChar) { 5859 case '0': info.type = BackupAgent.TYPE_FILE; break; 5860 case '5': { 5861 info.type = BackupAgent.TYPE_DIRECTORY; 5862 if (info.size != 0) { 5863 Slog.w(TAG, "Directory entry with nonzero size in header"); 5864 info.size = 0; 5865 } 5866 break; 5867 } 5868 case 0: { 5869 // presume EOF 5870 if (MORE_DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 5871 return null; 5872 } 5873 default: { 5874 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 5875 throw new IOException("Unknown entity type " + typeChar); 5876 } 5877 } 5878 5879 // Parse out the path 5880 // 5881 // first: apps/shared/unrecognized 5882 if (FullBackup.SHARED_PREFIX.regionMatches(0, 5883 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 5884 // File in shared storage. !!! TODO: implement this. 5885 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 5886 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 5887 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 5888 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 5889 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 5890 info.path, 0, FullBackup.APPS_PREFIX.length())) { 5891 // App content! Parse out the package name and domain 5892 5893 // strip the apps/ prefix 5894 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 5895 5896 // extract the package name 5897 int slash = info.path.indexOf('/'); 5898 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 5899 info.packageName = info.path.substring(0, slash); 5900 info.path = info.path.substring(slash+1); 5901 5902 // if it's a manifest or metadata payload we're done, otherwise parse 5903 // out the domain into which the file will be restored 5904 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 5905 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 5906 slash = info.path.indexOf('/'); 5907 if (slash < 0) { 5908 throw new IOException("Illegal semantic path in non-manifest " 5909 + info.path); 5910 } 5911 info.domain = info.path.substring(0, slash); 5912 info.path = info.path.substring(slash + 1); 5913 } 5914 } 5915 } catch (IOException e) { 5916 if (DEBUG) { 5917 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 5918 if (MORE_DEBUG) { 5919 HEXLOG(block); 5920 } 5921 } 5922 throw e; 5923 } 5924 } 5925 return info; 5926 } 5927 5928 private boolean isRestorableFile(FileMetadata info) { 5929 if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { 5930 if (MORE_DEBUG) { 5931 Slog.i(TAG, "Dropping cache file path " + info.path); 5932 } 5933 return false; 5934 } 5935 5936 if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) { 5937 // It's possible this is "no-backup" dir contents in an archive stream 5938 // produced on a device running a version of the OS that predates that 5939 // API. Respect the no-backup intention and don't let the data get to 5940 // the app. 5941 if (info.path.startsWith("no_backup/")) { 5942 if (MORE_DEBUG) { 5943 Slog.i(TAG, "Dropping no_backup file path " + info.path); 5944 } 5945 return false; 5946 } 5947 } 5948 5949 // The path needs to be canonical 5950 if (info.path.contains("..") || info.path.contains("//")) { 5951 if (MORE_DEBUG) { 5952 Slog.w(TAG, "Dropping invalid path " + info.path); 5953 } 5954 return false; 5955 } 5956 5957 // Otherwise we think this file is good to go 5958 return true; 5959 } 5960 5961 private void HEXLOG(byte[] block) { 5962 int offset = 0; 5963 int todo = block.length; 5964 StringBuilder buf = new StringBuilder(64); 5965 while (todo > 0) { 5966 buf.append(String.format("%04x ", offset)); 5967 int numThisLine = (todo > 16) ? 16 : todo; 5968 for (int i = 0; i < numThisLine; i++) { 5969 buf.append(String.format("%02x ", block[offset+i])); 5970 } 5971 Slog.i("hexdump", buf.toString()); 5972 buf.setLength(0); 5973 todo -= numThisLine; 5974 offset += numThisLine; 5975 } 5976 } 5977 5978 // Read exactly the given number of bytes into a buffer at the stated offset. 5979 // Returns false if EOF is encountered before the requested number of bytes 5980 // could be read. 5981 int readExactly(InputStream in, byte[] buffer, int offset, int size) 5982 throws IOException { 5983 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 5984if (MORE_DEBUG) Slog.i(TAG, " ... readExactly(" + size + ") called"); 5985 int soFar = 0; 5986 while (soFar < size) { 5987 int nRead = in.read(buffer, offset + soFar, size - soFar); 5988 if (nRead <= 0) { 5989 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 5990 break; 5991 } 5992 soFar += nRead; 5993if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar)); 5994 } 5995 return soFar; 5996 } 5997 5998 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 5999 final int got = readExactly(instream, block, 0, 512); 6000 if (got == 0) return false; // Clean EOF 6001 if (got < 512) throw new IOException("Unable to read full block header"); 6002 mBytes += 512; 6003 return true; 6004 } 6005 6006 // overwrites 'info' fields based on the pax extended header 6007 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 6008 throws IOException { 6009 // We should never see a pax extended header larger than this 6010 if (info.size > 32*1024) { 6011 Slog.w(TAG, "Suspiciously large pax header size " + info.size 6012 + " - aborting"); 6013 throw new IOException("Sanity failure: pax header size " + info.size); 6014 } 6015 6016 // read whole blocks, not just the content size 6017 int numBlocks = (int)((info.size + 511) >> 9); 6018 byte[] data = new byte[numBlocks * 512]; 6019 if (readExactly(instream, data, 0, data.length) < data.length) { 6020 throw new IOException("Unable to read full pax header"); 6021 } 6022 mBytes += data.length; 6023 6024 final int contentSize = (int) info.size; 6025 int offset = 0; 6026 do { 6027 // extract the line at 'offset' 6028 int eol = offset+1; 6029 while (eol < contentSize && data[eol] != ' ') eol++; 6030 if (eol >= contentSize) { 6031 // error: we just hit EOD looking for the end of the size field 6032 throw new IOException("Invalid pax data"); 6033 } 6034 // eol points to the space between the count and the key 6035 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 6036 int key = eol + 1; // start of key=value 6037 eol = offset + linelen - 1; // trailing LF 6038 int value; 6039 for (value = key+1; data[value] != '=' && value <= eol; value++); 6040 if (value > eol) { 6041 throw new IOException("Invalid pax declaration"); 6042 } 6043 6044 // pax requires that key/value strings be in UTF-8 6045 String keyStr = new String(data, key, value-key, "UTF-8"); 6046 // -1 to strip the trailing LF 6047 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 6048 6049 if ("path".equals(keyStr)) { 6050 info.path = valStr; 6051 } else if ("size".equals(keyStr)) { 6052 info.size = Long.parseLong(valStr); 6053 } else { 6054 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 6055 } 6056 6057 offset += linelen; 6058 } while (offset < contentSize); 6059 6060 return true; 6061 } 6062 6063 long extractRadix(byte[] data, int offset, int maxChars, int radix) 6064 throws IOException { 6065 long value = 0; 6066 final int end = offset + maxChars; 6067 for (int i = offset; i < end; i++) { 6068 final byte b = data[i]; 6069 // Numeric fields in tar can terminate with either NUL or SPC 6070 if (b == 0 || b == ' ') break; 6071 if (b < '0' || b > ('0' + radix - 1)) { 6072 throw new IOException("Invalid number in header: '" + (char)b 6073 + "' for radix " + radix); 6074 } 6075 value = radix * value + (b - '0'); 6076 } 6077 return value; 6078 } 6079 6080 String extractString(byte[] data, int offset, int maxChars) throws IOException { 6081 final int end = offset + maxChars; 6082 int eos = offset; 6083 // tar string fields terminate early with a NUL 6084 while (eos < end && data[eos] != 0) eos++; 6085 return new String(data, offset, eos-offset, "US-ASCII"); 6086 } 6087 6088 void sendStartRestore() { 6089 if (mObserver != null) { 6090 try { 6091 mObserver.onStartRestore(); 6092 } catch (RemoteException e) { 6093 Slog.w(TAG, "full restore observer went away: startRestore"); 6094 mObserver = null; 6095 } 6096 } 6097 } 6098 6099 void sendOnRestorePackage(String name) { 6100 if (mObserver != null) { 6101 try { 6102 // TODO: use a more user-friendly name string 6103 mObserver.onRestorePackage(name); 6104 } catch (RemoteException e) { 6105 Slog.w(TAG, "full restore observer went away: restorePackage"); 6106 mObserver = null; 6107 } 6108 } 6109 } 6110 6111 void sendEndRestore() { 6112 if (mObserver != null) { 6113 try { 6114 mObserver.onEndRestore(); 6115 } catch (RemoteException e) { 6116 Slog.w(TAG, "full restore observer went away: endRestore"); 6117 mObserver = null; 6118 } 6119 } 6120 } 6121 } 6122 6123 // ***** end new engine class *** 6124 6125 class PerformAdbRestoreTask implements Runnable { 6126 ParcelFileDescriptor mInputFile; 6127 String mCurrentPassword; 6128 String mDecryptPassword; 6129 IFullBackupRestoreObserver mObserver; 6130 AtomicBoolean mLatchObject; 6131 IBackupAgent mAgent; 6132 String mAgentPackage; 6133 ApplicationInfo mTargetApp; 6134 FullBackupObbConnection mObbConnection = null; 6135 ParcelFileDescriptor[] mPipes = null; 6136 byte[] mWidgetData = null; 6137 6138 long mBytes; 6139 6140 // possible handling states for a given package in the restore dataset 6141 final HashMap<String, RestorePolicy> mPackagePolicies 6142 = new HashMap<String, RestorePolicy>(); 6143 6144 // installer package names for each encountered app, derived from the manifests 6145 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 6146 6147 // Signatures for a given package found in its manifest file 6148 final HashMap<String, Signature[]> mManifestSignatures 6149 = new HashMap<String, Signature[]>(); 6150 6151 // Packages we've already wiped data on when restoring their first file 6152 final HashSet<String> mClearedPackages = new HashSet<String>(); 6153 6154 PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword, 6155 IFullBackupRestoreObserver observer, AtomicBoolean latch) { 6156 mInputFile = fd; 6157 mCurrentPassword = curPassword; 6158 mDecryptPassword = decryptPassword; 6159 mObserver = observer; 6160 mLatchObject = latch; 6161 mAgent = null; 6162 mAgentPackage = null; 6163 mTargetApp = null; 6164 mObbConnection = new FullBackupObbConnection(); 6165 6166 // Which packages we've already wiped data on. We prepopulate this 6167 // with a whitelist of packages known to be unclearable. 6168 mClearedPackages.add("android"); 6169 mClearedPackages.add(SETTINGS_PACKAGE); 6170 } 6171 6172 class RestoreFileRunnable implements Runnable { 6173 IBackupAgent mAgent; 6174 FileMetadata mInfo; 6175 ParcelFileDescriptor mSocket; 6176 int mToken; 6177 6178 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 6179 ParcelFileDescriptor socket, int token) throws IOException { 6180 mAgent = agent; 6181 mInfo = info; 6182 mToken = token; 6183 6184 // This class is used strictly for process-local binder invocations. The 6185 // semantics of ParcelFileDescriptor differ in this case; in particular, we 6186 // do not automatically get a 'dup'ed descriptor that we can can continue 6187 // to use asynchronously from the caller. So, we make sure to dup it ourselves 6188 // before proceeding to do the restore. 6189 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 6190 } 6191 6192 @Override 6193 public void run() { 6194 try { 6195 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 6196 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 6197 mToken, mBackupManagerBinder); 6198 } catch (RemoteException e) { 6199 // never happens; this is used strictly for local binder calls 6200 } 6201 } 6202 } 6203 6204 @Override 6205 public void run() { 6206 Slog.i(TAG, "--- Performing full-dataset restore ---"); 6207 mObbConnection.establish(); 6208 sendStartRestore(); 6209 6210 // Are we able to restore shared-storage data? 6211 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 6212 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT); 6213 } 6214 6215 FileInputStream rawInStream = null; 6216 DataInputStream rawDataIn = null; 6217 try { 6218 if (!backupPasswordMatches(mCurrentPassword)) { 6219 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 6220 return; 6221 } 6222 6223 mBytes = 0; 6224 byte[] buffer = new byte[32 * 1024]; 6225 rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); 6226 rawDataIn = new DataInputStream(rawInStream); 6227 6228 // First, parse out the unencrypted/uncompressed header 6229 boolean compressed = false; 6230 InputStream preCompressStream = rawInStream; 6231 final InputStream in; 6232 6233 boolean okay = false; 6234 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length(); 6235 byte[] streamHeader = new byte[headerLen]; 6236 rawDataIn.readFully(streamHeader); 6237 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8"); 6238 if (Arrays.equals(magicBytes, streamHeader)) { 6239 // okay, header looks good. now parse out the rest of the fields. 6240 String s = readHeaderLine(rawInStream); 6241 final int archiveVersion = Integer.parseInt(s); 6242 if (archiveVersion <= BACKUP_FILE_VERSION) { 6243 // okay, it's a version we recognize. if it's version 1, we may need 6244 // to try two different PBKDF2 regimes to compare checksums. 6245 final boolean pbkdf2Fallback = (archiveVersion == 1); 6246 6247 s = readHeaderLine(rawInStream); 6248 compressed = (Integer.parseInt(s) != 0); 6249 s = readHeaderLine(rawInStream); 6250 if (s.equals("none")) { 6251 // no more header to parse; we're good to go 6252 okay = true; 6253 } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) { 6254 preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback, 6255 rawInStream); 6256 if (preCompressStream != null) { 6257 okay = true; 6258 } 6259 } else Slog.w(TAG, "Archive is encrypted but no password given"); 6260 } else Slog.w(TAG, "Wrong header version: " + s); 6261 } else Slog.w(TAG, "Didn't read the right header magic"); 6262 6263 if (!okay) { 6264 Slog.w(TAG, "Invalid restore data; aborting."); 6265 return; 6266 } 6267 6268 // okay, use the right stream layer based on compression 6269 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream; 6270 6271 boolean didRestore; 6272 do { 6273 didRestore = restoreOneFile(in, buffer); 6274 } while (didRestore); 6275 6276 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); 6277 } catch (IOException e) { 6278 Slog.e(TAG, "Unable to read restore input"); 6279 } finally { 6280 tearDownPipes(); 6281 tearDownAgent(mTargetApp); 6282 6283 try { 6284 if (rawDataIn != null) rawDataIn.close(); 6285 if (rawInStream != null) rawInStream.close(); 6286 mInputFile.close(); 6287 } catch (IOException e) { 6288 Slog.w(TAG, "Close of restore data pipe threw", e); 6289 /* nothing we can do about this */ 6290 } 6291 synchronized (mCurrentOpLock) { 6292 mCurrentOperations.clear(); 6293 } 6294 synchronized (mLatchObject) { 6295 mLatchObject.set(true); 6296 mLatchObject.notifyAll(); 6297 } 6298 mObbConnection.tearDown(); 6299 sendEndRestore(); 6300 Slog.d(TAG, "Full restore pass complete."); 6301 mWakelock.release(); 6302 } 6303 } 6304 6305 String readHeaderLine(InputStream in) throws IOException { 6306 int c; 6307 StringBuilder buffer = new StringBuilder(80); 6308 while ((c = in.read()) >= 0) { 6309 if (c == '\n') break; // consume and discard the newlines 6310 buffer.append((char)c); 6311 } 6312 return buffer.toString(); 6313 } 6314 6315 InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt, 6316 int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream, 6317 boolean doLog) { 6318 InputStream result = null; 6319 6320 try { 6321 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 6322 SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt, 6323 rounds); 6324 byte[] IV = hexToByteArray(userIvHex); 6325 IvParameterSpec ivSpec = new IvParameterSpec(IV); 6326 c.init(Cipher.DECRYPT_MODE, 6327 new SecretKeySpec(userKey.getEncoded(), "AES"), 6328 ivSpec); 6329 byte[] mkCipher = hexToByteArray(masterKeyBlobHex); 6330 byte[] mkBlob = c.doFinal(mkCipher); 6331 6332 // first, the master key IV 6333 int offset = 0; 6334 int len = mkBlob[offset++]; 6335 IV = Arrays.copyOfRange(mkBlob, offset, offset + len); 6336 offset += len; 6337 // then the master key itself 6338 len = mkBlob[offset++]; 6339 byte[] mk = Arrays.copyOfRange(mkBlob, 6340 offset, offset + len); 6341 offset += len; 6342 // and finally the master key checksum hash 6343 len = mkBlob[offset++]; 6344 byte[] mkChecksum = Arrays.copyOfRange(mkBlob, 6345 offset, offset + len); 6346 6347 // now validate the decrypted master key against the checksum 6348 byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds); 6349 if (Arrays.equals(calculatedCk, mkChecksum)) { 6350 ivSpec = new IvParameterSpec(IV); 6351 c.init(Cipher.DECRYPT_MODE, 6352 new SecretKeySpec(mk, "AES"), 6353 ivSpec); 6354 // Only if all of the above worked properly will 'result' be assigned 6355 result = new CipherInputStream(rawInStream, c); 6356 } else if (doLog) Slog.w(TAG, "Incorrect password"); 6357 } catch (InvalidAlgorithmParameterException e) { 6358 if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e); 6359 } catch (BadPaddingException e) { 6360 // This case frequently occurs when the wrong password is used to decrypt 6361 // the master key. Use the identical "incorrect password" log text as is 6362 // used in the checksum failure log in order to avoid providing additional 6363 // information to an attacker. 6364 if (doLog) Slog.w(TAG, "Incorrect password"); 6365 } catch (IllegalBlockSizeException e) { 6366 if (doLog) Slog.w(TAG, "Invalid block size in master key"); 6367 } catch (NoSuchAlgorithmException e) { 6368 if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!"); 6369 } catch (NoSuchPaddingException e) { 6370 if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!"); 6371 } catch (InvalidKeyException e) { 6372 if (doLog) Slog.w(TAG, "Illegal password; aborting"); 6373 } 6374 6375 return result; 6376 } 6377 6378 InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback, 6379 InputStream rawInStream) { 6380 InputStream result = null; 6381 try { 6382 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) { 6383 6384 String userSaltHex = readHeaderLine(rawInStream); // 5 6385 byte[] userSalt = hexToByteArray(userSaltHex); 6386 6387 String ckSaltHex = readHeaderLine(rawInStream); // 6 6388 byte[] ckSalt = hexToByteArray(ckSaltHex); 6389 6390 int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 6391 String userIvHex = readHeaderLine(rawInStream); // 8 6392 6393 String masterKeyBlobHex = readHeaderLine(rawInStream); // 9 6394 6395 // decrypt the master key blob 6396 result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt, 6397 rounds, userIvHex, masterKeyBlobHex, rawInStream, false); 6398 if (result == null && pbkdf2Fallback) { 6399 result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt, 6400 rounds, userIvHex, masterKeyBlobHex, rawInStream, true); 6401 } 6402 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName); 6403 } catch (NumberFormatException e) { 6404 Slog.w(TAG, "Can't parse restore data header"); 6405 } catch (IOException e) { 6406 Slog.w(TAG, "Can't read input header"); 6407 } 6408 6409 return result; 6410 } 6411 6412 boolean restoreOneFile(InputStream instream, byte[] buffer) { 6413 FileMetadata info; 6414 try { 6415 info = readTarHeaders(instream); 6416 if (info != null) { 6417 if (MORE_DEBUG) { 6418 dumpFileMetadata(info); 6419 } 6420 6421 final String pkg = info.packageName; 6422 if (!pkg.equals(mAgentPackage)) { 6423 // okay, change in package; set up our various 6424 // bookkeeping if we haven't seen it yet 6425 if (!mPackagePolicies.containsKey(pkg)) { 6426 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6427 } 6428 6429 // Clean up the previous agent relationship if necessary, 6430 // and let the observer know we're considering a new app. 6431 if (mAgent != null) { 6432 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 6433 // Now we're really done 6434 tearDownPipes(); 6435 tearDownAgent(mTargetApp); 6436 mTargetApp = null; 6437 mAgentPackage = null; 6438 } 6439 } 6440 6441 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 6442 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 6443 mPackageInstallers.put(pkg, info.installerPackageName); 6444 // We've read only the manifest content itself at this point, 6445 // so consume the footer before looping around to the next 6446 // input file 6447 skipTarPadding(info.size, instream); 6448 sendOnRestorePackage(pkg); 6449 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 6450 // Metadata blobs! 6451 readMetadata(info, instream); 6452 skipTarPadding(info.size, instream); 6453 } else { 6454 // Non-manifest, so it's actual file data. Is this a package 6455 // we're ignoring? 6456 boolean okay = true; 6457 RestorePolicy policy = mPackagePolicies.get(pkg); 6458 switch (policy) { 6459 case IGNORE: 6460 okay = false; 6461 break; 6462 6463 case ACCEPT_IF_APK: 6464 // If we're in accept-if-apk state, then the first file we 6465 // see MUST be the apk. 6466 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 6467 if (DEBUG) Slog.d(TAG, "APK file; installing"); 6468 // Try to install the app. 6469 String installerName = mPackageInstallers.get(pkg); 6470 okay = installApk(info, installerName, instream); 6471 // good to go; promote to ACCEPT 6472 mPackagePolicies.put(pkg, (okay) 6473 ? RestorePolicy.ACCEPT 6474 : RestorePolicy.IGNORE); 6475 // At this point we've consumed this file entry 6476 // ourselves, so just strip the tar footer and 6477 // go on to the next file in the input stream 6478 skipTarPadding(info.size, instream); 6479 return true; 6480 } else { 6481 // File data before (or without) the apk. We can't 6482 // handle it coherently in this case so ignore it. 6483 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6484 okay = false; 6485 } 6486 break; 6487 6488 case ACCEPT: 6489 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 6490 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 6491 // we can take the data without the apk, so we 6492 // *want* to do so. skip the apk by declaring this 6493 // one file not-okay without changing the restore 6494 // policy for the package. 6495 okay = false; 6496 } 6497 break; 6498 6499 default: 6500 // Something has gone dreadfully wrong when determining 6501 // the restore policy from the manifest. Ignore the 6502 // rest of this package's data. 6503 Slog.e(TAG, "Invalid policy from manifest"); 6504 okay = false; 6505 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6506 break; 6507 } 6508 6509 // The path needs to be canonical 6510 if (info.path.contains("..") || info.path.contains("//")) { 6511 if (MORE_DEBUG) { 6512 Slog.w(TAG, "Dropping invalid path " + info.path); 6513 } 6514 okay = false; 6515 } 6516 6517 // If the policy is satisfied, go ahead and set up to pipe the 6518 // data to the agent. 6519 if (DEBUG && okay && mAgent != null) { 6520 Slog.i(TAG, "Reusing existing agent instance"); 6521 } 6522 if (okay && mAgent == null) { 6523 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 6524 6525 try { 6526 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 6527 6528 // If we haven't sent any data to this app yet, we probably 6529 // need to clear it first. Check that. 6530 if (!mClearedPackages.contains(pkg)) { 6531 // apps with their own backup agents are 6532 // responsible for coherently managing a full 6533 // restore. 6534 if (mTargetApp.backupAgentName == null) { 6535 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 6536 clearApplicationDataSynchronous(pkg); 6537 } else { 6538 if (DEBUG) Slog.d(TAG, "backup agent (" 6539 + mTargetApp.backupAgentName + ") => no clear"); 6540 } 6541 mClearedPackages.add(pkg); 6542 } else { 6543 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 6544 } 6545 6546 // All set; now set up the IPC and launch the agent 6547 setUpPipes(); 6548 mAgent = bindToAgentSynchronous(mTargetApp, 6549 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 6550 mAgentPackage = pkg; 6551 } catch (IOException e) { 6552 // fall through to error handling 6553 } catch (NameNotFoundException e) { 6554 // fall through to error handling 6555 } 6556 6557 if (mAgent == null) { 6558 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 6559 okay = false; 6560 tearDownPipes(); 6561 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6562 } 6563 } 6564 6565 // Sanity check: make sure we never give data to the wrong app. This 6566 // should never happen but a little paranoia here won't go amiss. 6567 if (okay && !pkg.equals(mAgentPackage)) { 6568 Slog.e(TAG, "Restoring data for " + pkg 6569 + " but agent is for " + mAgentPackage); 6570 okay = false; 6571 } 6572 6573 // At this point we have an agent ready to handle the full 6574 // restore data as well as a pipe for sending data to 6575 // that agent. Tell the agent to start reading from the 6576 // pipe. 6577 if (okay) { 6578 boolean agentSuccess = true; 6579 long toCopy = info.size; 6580 final int token = generateToken(); 6581 try { 6582 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 6583 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 6584 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 6585 + " : " + info.path); 6586 mObbConnection.restoreObbFile(pkg, mPipes[0], 6587 info.size, info.type, info.path, info.mode, 6588 info.mtime, token, mBackupManagerBinder); 6589 } else { 6590 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 6591 + info.path); 6592 // fire up the app's agent listening on the socket. If 6593 // the agent is running in the system process we can't 6594 // just invoke it asynchronously, so we provide a thread 6595 // for it here. 6596 if (mTargetApp.processName.equals("system")) { 6597 Slog.d(TAG, "system process agent - spinning a thread"); 6598 RestoreFileRunnable runner = new RestoreFileRunnable( 6599 mAgent, info, mPipes[0], token); 6600 new Thread(runner, "restore-sys-runner").start(); 6601 } else { 6602 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 6603 info.domain, info.path, info.mode, info.mtime, 6604 token, mBackupManagerBinder); 6605 } 6606 } 6607 } catch (IOException e) { 6608 // couldn't dup the socket for a process-local restore 6609 Slog.d(TAG, "Couldn't establish restore"); 6610 agentSuccess = false; 6611 okay = false; 6612 } catch (RemoteException e) { 6613 // whoops, remote entity went away. We'll eat the content 6614 // ourselves, then, and not copy it over. 6615 Slog.e(TAG, "Agent crashed during full restore"); 6616 agentSuccess = false; 6617 okay = false; 6618 } 6619 6620 // Copy over the data if the agent is still good 6621 if (okay) { 6622 boolean pipeOkay = true; 6623 FileOutputStream pipe = new FileOutputStream( 6624 mPipes[1].getFileDescriptor()); 6625 while (toCopy > 0) { 6626 int toRead = (toCopy > buffer.length) 6627 ? buffer.length : (int)toCopy; 6628 int nRead = instream.read(buffer, 0, toRead); 6629 if (nRead >= 0) mBytes += nRead; 6630 if (nRead <= 0) break; 6631 toCopy -= nRead; 6632 6633 // send it to the output pipe as long as things 6634 // are still good 6635 if (pipeOkay) { 6636 try { 6637 pipe.write(buffer, 0, nRead); 6638 } catch (IOException e) { 6639 Slog.e(TAG, "Failed to write to restore pipe", e); 6640 pipeOkay = false; 6641 } 6642 } 6643 } 6644 6645 // done sending that file! Now we just need to consume 6646 // the delta from info.size to the end of block. 6647 skipTarPadding(info.size, instream); 6648 6649 // and now that we've sent it all, wait for the remote 6650 // side to acknowledge receipt 6651 agentSuccess = waitUntilOperationComplete(token); 6652 } 6653 6654 // okay, if the remote end failed at any point, deal with 6655 // it by ignoring the rest of the restore on it 6656 if (!agentSuccess) { 6657 mBackupHandler.removeMessages(MSG_TIMEOUT); 6658 tearDownPipes(); 6659 tearDownAgent(mTargetApp); 6660 mAgent = null; 6661 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6662 } 6663 } 6664 6665 // Problems setting up the agent communication, or an already- 6666 // ignored package: skip to the next tar stream entry by 6667 // reading and discarding this file. 6668 if (!okay) { 6669 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 6670 long bytesToConsume = (info.size + 511) & ~511; 6671 while (bytesToConsume > 0) { 6672 int toRead = (bytesToConsume > buffer.length) 6673 ? buffer.length : (int)bytesToConsume; 6674 long nRead = instream.read(buffer, 0, toRead); 6675 if (nRead >= 0) mBytes += nRead; 6676 if (nRead <= 0) break; 6677 bytesToConsume -= nRead; 6678 } 6679 } 6680 } 6681 } 6682 } catch (IOException e) { 6683 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 6684 // treat as EOF 6685 info = null; 6686 } 6687 6688 return (info != null); 6689 } 6690 6691 void setUpPipes() throws IOException { 6692 mPipes = ParcelFileDescriptor.createPipe(); 6693 } 6694 6695 void tearDownPipes() { 6696 if (mPipes != null) { 6697 try { 6698 mPipes[0].close(); 6699 mPipes[0] = null; 6700 mPipes[1].close(); 6701 mPipes[1] = null; 6702 } catch (IOException e) { 6703 Slog.w(TAG, "Couldn't close agent pipes", e); 6704 } 6705 mPipes = null; 6706 } 6707 } 6708 6709 void tearDownAgent(ApplicationInfo app) { 6710 if (mAgent != null) { 6711 try { 6712 // unbind and tidy up even on timeout or failure, just in case 6713 mActivityManager.unbindBackupAgent(app); 6714 6715 // The agent was running with a stub Application object, so shut it down. 6716 // !!! We hardcode the confirmation UI's package name here rather than use a 6717 // manifest flag! TODO something less direct. 6718 if (app.uid != Process.SYSTEM_UID 6719 && !app.packageName.equals("com.android.backupconfirm")) { 6720 if (DEBUG) Slog.d(TAG, "Killing host process"); 6721 mActivityManager.killApplicationProcess(app.processName, app.uid); 6722 } else { 6723 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 6724 } 6725 } catch (RemoteException e) { 6726 Slog.d(TAG, "Lost app trying to shut down"); 6727 } 6728 mAgent = null; 6729 } 6730 } 6731 6732 class RestoreInstallObserver extends PackageInstallObserver { 6733 final AtomicBoolean mDone = new AtomicBoolean(); 6734 String mPackageName; 6735 int mResult; 6736 6737 public void reset() { 6738 synchronized (mDone) { 6739 mDone.set(false); 6740 } 6741 } 6742 6743 public void waitForCompletion() { 6744 synchronized (mDone) { 6745 while (mDone.get() == false) { 6746 try { 6747 mDone.wait(); 6748 } catch (InterruptedException e) { } 6749 } 6750 } 6751 } 6752 6753 int getResult() { 6754 return mResult; 6755 } 6756 6757 @Override 6758 public void onPackageInstalled(String packageName, int returnCode, 6759 String msg, Bundle extras) { 6760 synchronized (mDone) { 6761 mResult = returnCode; 6762 mPackageName = packageName; 6763 mDone.set(true); 6764 mDone.notifyAll(); 6765 } 6766 } 6767 } 6768 6769 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 6770 final AtomicBoolean mDone = new AtomicBoolean(); 6771 int mResult; 6772 6773 public void reset() { 6774 synchronized (mDone) { 6775 mDone.set(false); 6776 } 6777 } 6778 6779 public void waitForCompletion() { 6780 synchronized (mDone) { 6781 while (mDone.get() == false) { 6782 try { 6783 mDone.wait(); 6784 } catch (InterruptedException e) { } 6785 } 6786 } 6787 } 6788 6789 @Override 6790 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 6791 synchronized (mDone) { 6792 mResult = returnCode; 6793 mDone.set(true); 6794 mDone.notifyAll(); 6795 } 6796 } 6797 } 6798 6799 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 6800 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 6801 6802 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 6803 boolean okay = true; 6804 6805 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 6806 6807 // The file content is an .apk file. Copy it out to a staging location and 6808 // attempt to install it. 6809 File apkFile = new File(mDataDir, info.packageName); 6810 try { 6811 FileOutputStream apkStream = new FileOutputStream(apkFile); 6812 byte[] buffer = new byte[32 * 1024]; 6813 long size = info.size; 6814 while (size > 0) { 6815 long toRead = (buffer.length < size) ? buffer.length : size; 6816 int didRead = instream.read(buffer, 0, (int)toRead); 6817 if (didRead >= 0) mBytes += didRead; 6818 apkStream.write(buffer, 0, didRead); 6819 size -= didRead; 6820 } 6821 apkStream.close(); 6822 6823 // make sure the installer can read it 6824 apkFile.setReadable(true, false); 6825 6826 // Now install it 6827 Uri packageUri = Uri.fromFile(apkFile); 6828 mInstallObserver.reset(); 6829 mPackageManager.installPackage(packageUri, mInstallObserver, 6830 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 6831 installerPackage); 6832 mInstallObserver.waitForCompletion(); 6833 6834 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 6835 // The only time we continue to accept install of data even if the 6836 // apk install failed is if we had already determined that we could 6837 // accept the data regardless. 6838 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 6839 okay = false; 6840 } 6841 } else { 6842 // Okay, the install succeeded. Make sure it was the right app. 6843 boolean uninstall = false; 6844 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 6845 Slog.w(TAG, "Restore stream claimed to include apk for " 6846 + info.packageName + " but apk was really " 6847 + mInstallObserver.mPackageName); 6848 // delete the package we just put in place; it might be fraudulent 6849 okay = false; 6850 uninstall = true; 6851 } else { 6852 try { 6853 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 6854 PackageManager.GET_SIGNATURES); 6855 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 6856 Slog.w(TAG, "Restore stream contains apk of package " 6857 + info.packageName + " but it disallows backup/restore"); 6858 okay = false; 6859 } else { 6860 // So far so good -- do the signatures match the manifest? 6861 Signature[] sigs = mManifestSignatures.get(info.packageName); 6862 if (signaturesMatch(sigs, pkg)) { 6863 // If this is a system-uid app without a declared backup agent, 6864 // don't restore any of the file data. 6865 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 6866 && (pkg.applicationInfo.backupAgentName == null)) { 6867 Slog.w(TAG, "Installed app " + info.packageName 6868 + " has restricted uid and no agent"); 6869 okay = false; 6870 } 6871 } else { 6872 Slog.w(TAG, "Installed app " + info.packageName 6873 + " signatures do not match restore manifest"); 6874 okay = false; 6875 uninstall = true; 6876 } 6877 } 6878 } catch (NameNotFoundException e) { 6879 Slog.w(TAG, "Install of package " + info.packageName 6880 + " succeeded but now not found"); 6881 okay = false; 6882 } 6883 } 6884 6885 // If we're not okay at this point, we need to delete the package 6886 // that we just installed. 6887 if (uninstall) { 6888 mDeleteObserver.reset(); 6889 mPackageManager.deletePackage(mInstallObserver.mPackageName, 6890 mDeleteObserver, 0); 6891 mDeleteObserver.waitForCompletion(); 6892 } 6893 } 6894 } catch (IOException e) { 6895 Slog.e(TAG, "Unable to transcribe restored apk for install"); 6896 okay = false; 6897 } finally { 6898 apkFile.delete(); 6899 } 6900 6901 return okay; 6902 } 6903 6904 // Given an actual file content size, consume the post-content padding mandated 6905 // by the tar format. 6906 void skipTarPadding(long size, InputStream instream) throws IOException { 6907 long partial = (size + 512) % 512; 6908 if (partial > 0) { 6909 final int needed = 512 - (int)partial; 6910 byte[] buffer = new byte[needed]; 6911 if (readExactly(instream, buffer, 0, needed) == needed) { 6912 mBytes += needed; 6913 } else throw new IOException("Unexpected EOF in padding"); 6914 } 6915 } 6916 6917 // Read a widget metadata file, returning the restored blob 6918 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 6919 // Fail on suspiciously large widget dump files 6920 if (info.size > 64 * 1024) { 6921 throw new IOException("Metadata too big; corrupt? size=" + info.size); 6922 } 6923 6924 byte[] buffer = new byte[(int) info.size]; 6925 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 6926 mBytes += info.size; 6927 } else throw new IOException("Unexpected EOF in widget data"); 6928 6929 String[] str = new String[1]; 6930 int offset = extractLine(buffer, 0, str); 6931 int version = Integer.parseInt(str[0]); 6932 if (version == BACKUP_MANIFEST_VERSION) { 6933 offset = extractLine(buffer, offset, str); 6934 final String pkg = str[0]; 6935 if (info.packageName.equals(pkg)) { 6936 // Data checks out -- the rest of the buffer is a concatenation of 6937 // binary blobs as described in the comment at writeAppWidgetData() 6938 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 6939 offset, buffer.length - offset); 6940 DataInputStream in = new DataInputStream(bin); 6941 while (bin.available() > 0) { 6942 int token = in.readInt(); 6943 int size = in.readInt(); 6944 if (size > 64 * 1024) { 6945 throw new IOException("Datum " 6946 + Integer.toHexString(token) 6947 + " too big; corrupt? size=" + info.size); 6948 } 6949 switch (token) { 6950 case BACKUP_WIDGET_METADATA_TOKEN: 6951 { 6952 if (MORE_DEBUG) { 6953 Slog.i(TAG, "Got widget metadata for " + info.packageName); 6954 } 6955 mWidgetData = new byte[size]; 6956 in.read(mWidgetData); 6957 break; 6958 } 6959 default: 6960 { 6961 if (DEBUG) { 6962 Slog.i(TAG, "Ignoring metadata blob " 6963 + Integer.toHexString(token) 6964 + " for " + info.packageName); 6965 } 6966 in.skipBytes(size); 6967 break; 6968 } 6969 } 6970 } 6971 } else { 6972 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 6973 + " but widget data for " + pkg); 6974 } 6975 } else { 6976 Slog.w(TAG, "Unsupported metadata version " + version); 6977 } 6978 } 6979 6980 // Returns a policy constant; takes a buffer arg to reduce memory churn 6981 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 6982 throws IOException { 6983 // Fail on suspiciously large manifest files 6984 if (info.size > 64 * 1024) { 6985 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 6986 } 6987 6988 byte[] buffer = new byte[(int) info.size]; 6989 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 6990 mBytes += info.size; 6991 } else throw new IOException("Unexpected EOF in manifest"); 6992 6993 RestorePolicy policy = RestorePolicy.IGNORE; 6994 String[] str = new String[1]; 6995 int offset = 0; 6996 6997 try { 6998 offset = extractLine(buffer, offset, str); 6999 int version = Integer.parseInt(str[0]); 7000 if (version == BACKUP_MANIFEST_VERSION) { 7001 offset = extractLine(buffer, offset, str); 7002 String manifestPackage = str[0]; 7003 // TODO: handle <original-package> 7004 if (manifestPackage.equals(info.packageName)) { 7005 offset = extractLine(buffer, offset, str); 7006 version = Integer.parseInt(str[0]); // app version 7007 offset = extractLine(buffer, offset, str); 7008 // This is the platform version, which we don't use, but we parse it 7009 // as a safety against corruption in the manifest. 7010 Integer.parseInt(str[0]); 7011 offset = extractLine(buffer, offset, str); 7012 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 7013 offset = extractLine(buffer, offset, str); 7014 boolean hasApk = str[0].equals("1"); 7015 offset = extractLine(buffer, offset, str); 7016 int numSigs = Integer.parseInt(str[0]); 7017 if (numSigs > 0) { 7018 Signature[] sigs = new Signature[numSigs]; 7019 for (int i = 0; i < numSigs; i++) { 7020 offset = extractLine(buffer, offset, str); 7021 sigs[i] = new Signature(str[0]); 7022 } 7023 mManifestSignatures.put(info.packageName, sigs); 7024 7025 // Okay, got the manifest info we need... 7026 try { 7027 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 7028 info.packageName, PackageManager.GET_SIGNATURES); 7029 // Fall through to IGNORE if the app explicitly disallows backup 7030 final int flags = pkgInfo.applicationInfo.flags; 7031 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 7032 // Restore system-uid-space packages only if they have 7033 // defined a custom backup agent 7034 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 7035 || (pkgInfo.applicationInfo.backupAgentName != null)) { 7036 // Verify signatures against any installed version; if they 7037 // don't match, then we fall though and ignore the data. The 7038 // signatureMatch() method explicitly ignores the signature 7039 // check for packages installed on the system partition, because 7040 // such packages are signed with the platform cert instead of 7041 // the app developer's cert, so they're different on every 7042 // device. 7043 if (signaturesMatch(sigs, pkgInfo)) { 7044 if (pkgInfo.versionCode >= version) { 7045 Slog.i(TAG, "Sig + version match; taking data"); 7046 policy = RestorePolicy.ACCEPT; 7047 } else { 7048 // The data is from a newer version of the app than 7049 // is presently installed. That means we can only 7050 // use it if the matching apk is also supplied. 7051 Slog.d(TAG, "Data version " + version 7052 + " is newer than installed version " 7053 + pkgInfo.versionCode + " - requiring apk"); 7054 policy = RestorePolicy.ACCEPT_IF_APK; 7055 } 7056 } else { 7057 Slog.w(TAG, "Restore manifest signatures do not match " 7058 + "installed application for " + info.packageName); 7059 } 7060 } else { 7061 Slog.w(TAG, "Package " + info.packageName 7062 + " is system level with no agent"); 7063 } 7064 } else { 7065 if (DEBUG) Slog.i(TAG, "Restore manifest from " 7066 + info.packageName + " but allowBackup=false"); 7067 } 7068 } catch (NameNotFoundException e) { 7069 // Okay, the target app isn't installed. We can process 7070 // the restore properly only if the dataset provides the 7071 // apk file and we can successfully install it. 7072 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 7073 + " not installed; requiring apk in dataset"); 7074 policy = RestorePolicy.ACCEPT_IF_APK; 7075 } 7076 7077 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 7078 Slog.i(TAG, "Cannot restore package " + info.packageName 7079 + " without the matching .apk"); 7080 } 7081 } else { 7082 Slog.i(TAG, "Missing signature on backed-up package " 7083 + info.packageName); 7084 } 7085 } else { 7086 Slog.i(TAG, "Expected package " + info.packageName 7087 + " but restore manifest claims " + manifestPackage); 7088 } 7089 } else { 7090 Slog.i(TAG, "Unknown restore manifest version " + version 7091 + " for package " + info.packageName); 7092 } 7093 } catch (NumberFormatException e) { 7094 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 7095 } catch (IllegalArgumentException e) { 7096 Slog.w(TAG, e.getMessage()); 7097 } 7098 7099 return policy; 7100 } 7101 7102 // Builds a line from a byte buffer starting at 'offset', and returns 7103 // the index of the next unconsumed data in the buffer. 7104 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 7105 final int end = buffer.length; 7106 if (offset >= end) throw new IOException("Incomplete data"); 7107 7108 int pos; 7109 for (pos = offset; pos < end; pos++) { 7110 byte c = buffer[pos]; 7111 // at LF we declare end of line, and return the next char as the 7112 // starting point for the next time through 7113 if (c == '\n') { 7114 break; 7115 } 7116 } 7117 outStr[0] = new String(buffer, offset, pos - offset); 7118 pos++; // may be pointing an extra byte past the end but that's okay 7119 return pos; 7120 } 7121 7122 void dumpFileMetadata(FileMetadata info) { 7123 if (DEBUG) { 7124 StringBuilder b = new StringBuilder(128); 7125 7126 // mode string 7127 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 7128 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 7129 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 7130 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 7131 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 7132 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 7133 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 7134 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 7135 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 7136 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 7137 b.append(String.format(" %9d ", info.size)); 7138 7139 Date stamp = new Date(info.mtime); 7140 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 7141 7142 b.append(info.packageName); 7143 b.append(" :: "); 7144 b.append(info.domain); 7145 b.append(" :: "); 7146 b.append(info.path); 7147 7148 Slog.i(TAG, b.toString()); 7149 } 7150 } 7151 // Consume a tar file header block [sequence] and accumulate the relevant metadata 7152 FileMetadata readTarHeaders(InputStream instream) throws IOException { 7153 byte[] block = new byte[512]; 7154 FileMetadata info = null; 7155 7156 boolean gotHeader = readTarHeader(instream, block); 7157 if (gotHeader) { 7158 try { 7159 // okay, presume we're okay, and extract the various metadata 7160 info = new FileMetadata(); 7161 info.size = extractRadix(block, 124, 12, 8); 7162 info.mtime = extractRadix(block, 136, 12, 8); 7163 info.mode = extractRadix(block, 100, 8, 8); 7164 7165 info.path = extractString(block, 345, 155); // prefix 7166 String path = extractString(block, 0, 100); 7167 if (path.length() > 0) { 7168 if (info.path.length() > 0) info.path += '/'; 7169 info.path += path; 7170 } 7171 7172 // tar link indicator field: 1 byte at offset 156 in the header. 7173 int typeChar = block[156]; 7174 if (typeChar == 'x') { 7175 // pax extended header, so we need to read that 7176 gotHeader = readPaxExtendedHeader(instream, info); 7177 if (gotHeader) { 7178 // and after a pax extended header comes another real header -- read 7179 // that to find the real file type 7180 gotHeader = readTarHeader(instream, block); 7181 } 7182 if (!gotHeader) throw new IOException("Bad or missing pax header"); 7183 7184 typeChar = block[156]; 7185 } 7186 7187 switch (typeChar) { 7188 case '0': info.type = BackupAgent.TYPE_FILE; break; 7189 case '5': { 7190 info.type = BackupAgent.TYPE_DIRECTORY; 7191 if (info.size != 0) { 7192 Slog.w(TAG, "Directory entry with nonzero size in header"); 7193 info.size = 0; 7194 } 7195 break; 7196 } 7197 case 0: { 7198 // presume EOF 7199 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 7200 return null; 7201 } 7202 default: { 7203 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 7204 throw new IOException("Unknown entity type " + typeChar); 7205 } 7206 } 7207 7208 // Parse out the path 7209 // 7210 // first: apps/shared/unrecognized 7211 if (FullBackup.SHARED_PREFIX.regionMatches(0, 7212 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 7213 // File in shared storage. !!! TODO: implement this. 7214 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 7215 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 7216 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 7217 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 7218 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 7219 info.path, 0, FullBackup.APPS_PREFIX.length())) { 7220 // App content! Parse out the package name and domain 7221 7222 // strip the apps/ prefix 7223 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 7224 7225 // extract the package name 7226 int slash = info.path.indexOf('/'); 7227 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 7228 info.packageName = info.path.substring(0, slash); 7229 info.path = info.path.substring(slash+1); 7230 7231 // if it's a manifest or metadata payload we're done, otherwise parse 7232 // out the domain into which the file will be restored 7233 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 7234 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 7235 slash = info.path.indexOf('/'); 7236 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path); 7237 info.domain = info.path.substring(0, slash); 7238 info.path = info.path.substring(slash + 1); 7239 } 7240 } 7241 } catch (IOException e) { 7242 if (DEBUG) { 7243 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 7244 HEXLOG(block); 7245 } 7246 throw e; 7247 } 7248 } 7249 return info; 7250 } 7251 7252 private void HEXLOG(byte[] block) { 7253 int offset = 0; 7254 int todo = block.length; 7255 StringBuilder buf = new StringBuilder(64); 7256 while (todo > 0) { 7257 buf.append(String.format("%04x ", offset)); 7258 int numThisLine = (todo > 16) ? 16 : todo; 7259 for (int i = 0; i < numThisLine; i++) { 7260 buf.append(String.format("%02x ", block[offset+i])); 7261 } 7262 Slog.i("hexdump", buf.toString()); 7263 buf.setLength(0); 7264 todo -= numThisLine; 7265 offset += numThisLine; 7266 } 7267 } 7268 7269 // Read exactly the given number of bytes into a buffer at the stated offset. 7270 // Returns false if EOF is encountered before the requested number of bytes 7271 // could be read. 7272 int readExactly(InputStream in, byte[] buffer, int offset, int size) 7273 throws IOException { 7274 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 7275 7276 int soFar = 0; 7277 while (soFar < size) { 7278 int nRead = in.read(buffer, offset + soFar, size - soFar); 7279 if (nRead <= 0) { 7280 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 7281 break; 7282 } 7283 soFar += nRead; 7284 } 7285 return soFar; 7286 } 7287 7288 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 7289 final int got = readExactly(instream, block, 0, 512); 7290 if (got == 0) return false; // Clean EOF 7291 if (got < 512) throw new IOException("Unable to read full block header"); 7292 mBytes += 512; 7293 return true; 7294 } 7295 7296 // overwrites 'info' fields based on the pax extended header 7297 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 7298 throws IOException { 7299 // We should never see a pax extended header larger than this 7300 if (info.size > 32*1024) { 7301 Slog.w(TAG, "Suspiciously large pax header size " + info.size 7302 + " - aborting"); 7303 throw new IOException("Sanity failure: pax header size " + info.size); 7304 } 7305 7306 // read whole blocks, not just the content size 7307 int numBlocks = (int)((info.size + 511) >> 9); 7308 byte[] data = new byte[numBlocks * 512]; 7309 if (readExactly(instream, data, 0, data.length) < data.length) { 7310 throw new IOException("Unable to read full pax header"); 7311 } 7312 mBytes += data.length; 7313 7314 final int contentSize = (int) info.size; 7315 int offset = 0; 7316 do { 7317 // extract the line at 'offset' 7318 int eol = offset+1; 7319 while (eol < contentSize && data[eol] != ' ') eol++; 7320 if (eol >= contentSize) { 7321 // error: we just hit EOD looking for the end of the size field 7322 throw new IOException("Invalid pax data"); 7323 } 7324 // eol points to the space between the count and the key 7325 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 7326 int key = eol + 1; // start of key=value 7327 eol = offset + linelen - 1; // trailing LF 7328 int value; 7329 for (value = key+1; data[value] != '=' && value <= eol; value++); 7330 if (value > eol) { 7331 throw new IOException("Invalid pax declaration"); 7332 } 7333 7334 // pax requires that key/value strings be in UTF-8 7335 String keyStr = new String(data, key, value-key, "UTF-8"); 7336 // -1 to strip the trailing LF 7337 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 7338 7339 if ("path".equals(keyStr)) { 7340 info.path = valStr; 7341 } else if ("size".equals(keyStr)) { 7342 info.size = Long.parseLong(valStr); 7343 } else { 7344 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 7345 } 7346 7347 offset += linelen; 7348 } while (offset < contentSize); 7349 7350 return true; 7351 } 7352 7353 long extractRadix(byte[] data, int offset, int maxChars, int radix) 7354 throws IOException { 7355 long value = 0; 7356 final int end = offset + maxChars; 7357 for (int i = offset; i < end; i++) { 7358 final byte b = data[i]; 7359 // Numeric fields in tar can terminate with either NUL or SPC 7360 if (b == 0 || b == ' ') break; 7361 if (b < '0' || b > ('0' + radix - 1)) { 7362 throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix); 7363 } 7364 value = radix * value + (b - '0'); 7365 } 7366 return value; 7367 } 7368 7369 String extractString(byte[] data, int offset, int maxChars) throws IOException { 7370 final int end = offset + maxChars; 7371 int eos = offset; 7372 // tar string fields terminate early with a NUL 7373 while (eos < end && data[eos] != 0) eos++; 7374 return new String(data, offset, eos-offset, "US-ASCII"); 7375 } 7376 7377 void sendStartRestore() { 7378 if (mObserver != null) { 7379 try { 7380 mObserver.onStartRestore(); 7381 } catch (RemoteException e) { 7382 Slog.w(TAG, "full restore observer went away: startRestore"); 7383 mObserver = null; 7384 } 7385 } 7386 } 7387 7388 void sendOnRestorePackage(String name) { 7389 if (mObserver != null) { 7390 try { 7391 // TODO: use a more user-friendly name string 7392 mObserver.onRestorePackage(name); 7393 } catch (RemoteException e) { 7394 Slog.w(TAG, "full restore observer went away: restorePackage"); 7395 mObserver = null; 7396 } 7397 } 7398 } 7399 7400 void sendEndRestore() { 7401 if (mObserver != null) { 7402 try { 7403 mObserver.onEndRestore(); 7404 } catch (RemoteException e) { 7405 Slog.w(TAG, "full restore observer went away: endRestore"); 7406 mObserver = null; 7407 } 7408 } 7409 } 7410 } 7411 7412 // ----- Restore handling ----- 7413 7414 // new style: we only store the SHA-1 hashes of each sig, not the full block 7415 static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) { 7416 if (target == null) { 7417 return false; 7418 } 7419 7420 // If the target resides on the system partition, we allow it to restore 7421 // data from the like-named package in a restore set even if the signatures 7422 // do not match. (Unlike general applications, those flashed to the system 7423 // partition will be signed with the device's platform certificate, so on 7424 // different phones the same system app will have different signatures.) 7425 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 7426 if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 7427 return true; 7428 } 7429 7430 // Allow unsigned apps, but not signed on one device and unsigned on the other 7431 // !!! TODO: is this the right policy? 7432 Signature[] deviceSigs = target.signatures; 7433 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes 7434 + " device=" + deviceSigs); 7435 if ((storedSigHashes == null || storedSigHashes.size() == 0) 7436 && (deviceSigs == null || deviceSigs.length == 0)) { 7437 return true; 7438 } 7439 if (storedSigHashes == null || deviceSigs == null) { 7440 return false; 7441 } 7442 7443 // !!! TODO: this demands that every stored signature match one 7444 // that is present on device, and does not demand the converse. 7445 // Is this this right policy? 7446 final int nStored = storedSigHashes.size(); 7447 final int nDevice = deviceSigs.length; 7448 7449 // hash each on-device signature 7450 ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice); 7451 for (int i = 0; i < nDevice; i++) { 7452 deviceHashes.add(hashSignature(deviceSigs[i])); 7453 } 7454 7455 // now ensure that each stored sig (hash) matches an on-device sig (hash) 7456 for (int n = 0; n < nStored; n++) { 7457 boolean match = false; 7458 final byte[] storedHash = storedSigHashes.get(n); 7459 for (int i = 0; i < nDevice; i++) { 7460 if (Arrays.equals(storedHash, deviceHashes.get(i))) { 7461 match = true; 7462 break; 7463 } 7464 } 7465 // match is false when no on-device sig matched one of the stored ones 7466 if (!match) { 7467 return false; 7468 } 7469 } 7470 7471 return true; 7472 } 7473 7474 static byte[] hashSignature(Signature sig) { 7475 try { 7476 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 7477 digest.update(sig.toByteArray()); 7478 return digest.digest(); 7479 } catch (NoSuchAlgorithmException e) { 7480 Slog.w(TAG, "No SHA-256 algorithm found!"); 7481 } 7482 return null; 7483 } 7484 7485 // Old style: directly match the stored vs on device signature blocks 7486 static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 7487 if (target == null) { 7488 return false; 7489 } 7490 7491 // If the target resides on the system partition, we allow it to restore 7492 // data from the like-named package in a restore set even if the signatures 7493 // do not match. (Unlike general applications, those flashed to the system 7494 // partition will be signed with the device's platform certificate, so on 7495 // different phones the same system app will have different signatures.) 7496 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 7497 if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 7498 return true; 7499 } 7500 7501 // Allow unsigned apps, but not signed on one device and unsigned on the other 7502 // !!! TODO: is this the right policy? 7503 Signature[] deviceSigs = target.signatures; 7504 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs 7505 + " device=" + deviceSigs); 7506 if ((storedSigs == null || storedSigs.length == 0) 7507 && (deviceSigs == null || deviceSigs.length == 0)) { 7508 return true; 7509 } 7510 if (storedSigs == null || deviceSigs == null) { 7511 return false; 7512 } 7513 7514 // !!! TODO: this demands that every stored signature match one 7515 // that is present on device, and does not demand the converse. 7516 // Is this this right policy? 7517 int nStored = storedSigs.length; 7518 int nDevice = deviceSigs.length; 7519 7520 for (int i=0; i < nStored; i++) { 7521 boolean match = false; 7522 for (int j=0; j < nDevice; j++) { 7523 if (storedSigs[i].equals(deviceSigs[j])) { 7524 match = true; 7525 break; 7526 } 7527 } 7528 if (!match) { 7529 return false; 7530 } 7531 } 7532 return true; 7533 } 7534 7535 // Used by both incremental and full restore 7536 void restoreWidgetData(String packageName, byte[] widgetData) { 7537 // Apply the restored widget state and generate the ID update for the app 7538 // TODO: http://b/22388012 7539 AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM); 7540 } 7541 7542 // ***************************** 7543 // NEW UNIFIED RESTORE IMPLEMENTATION 7544 // ***************************** 7545 7546 // states of the unified-restore state machine 7547 enum UnifiedRestoreState { 7548 INITIAL, 7549 RUNNING_QUEUE, 7550 RESTORE_KEYVALUE, 7551 RESTORE_FULL, 7552 RESTORE_FINISHED, 7553 FINAL 7554 } 7555 7556 class PerformUnifiedRestoreTask implements BackupRestoreTask { 7557 // Transport we're working with to do the restore 7558 private IBackupTransport mTransport; 7559 7560 // Where per-transport saved state goes 7561 File mStateDir; 7562 7563 // Restore observer; may be null 7564 private IRestoreObserver mObserver; 7565 7566 // Token identifying the dataset to the transport 7567 private long mToken; 7568 7569 // When this is a restore-during-install, this is the token identifying the 7570 // operation to the Package Manager, and we must ensure that we let it know 7571 // when we're finished. 7572 private int mPmToken; 7573 7574 // Is this a whole-system restore, i.e. are we establishing a new ancestral 7575 // dataset to base future restore-at-install operations from? 7576 private boolean mIsSystemRestore; 7577 7578 // If this is a single-package restore, what package are we interested in? 7579 private PackageInfo mTargetPackage; 7580 7581 // In all cases, the calculated list of packages that we are trying to restore 7582 private List<PackageInfo> mAcceptSet; 7583 7584 // Our bookkeeping about the ancestral dataset 7585 private PackageManagerBackupAgent mPmAgent; 7586 7587 // Currently-bound backup agent for restore + restoreFinished purposes 7588 private IBackupAgent mAgent; 7589 7590 // What sort of restore we're doing now 7591 private RestoreDescription mRestoreDescription; 7592 7593 // The package we're currently restoring 7594 private PackageInfo mCurrentPackage; 7595 7596 // Widget-related data handled as part of this restore operation 7597 private byte[] mWidgetData; 7598 7599 // Number of apps restored in this pass 7600 private int mCount; 7601 7602 // When did we start? 7603 private long mStartRealtime; 7604 7605 // State machine progress 7606 private UnifiedRestoreState mState; 7607 7608 // How are things going? 7609 private int mStatus; 7610 7611 // Done? 7612 private boolean mFinished; 7613 7614 // Key/value: bookkeeping about staged data and files for agent access 7615 private File mBackupDataName; 7616 private File mStageName; 7617 private File mSavedStateName; 7618 private File mNewStateName; 7619 ParcelFileDescriptor mBackupData; 7620 ParcelFileDescriptor mNewState; 7621 7622 // Invariant: mWakelock is already held, and this task is responsible for 7623 // releasing it at the end of the restore operation. 7624 PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer, 7625 long restoreSetToken, PackageInfo targetPackage, int pmToken, 7626 boolean isFullSystemRestore, String[] filterSet) { 7627 mState = UnifiedRestoreState.INITIAL; 7628 mStartRealtime = SystemClock.elapsedRealtime(); 7629 7630 mTransport = transport; 7631 mObserver = observer; 7632 mToken = restoreSetToken; 7633 mPmToken = pmToken; 7634 mTargetPackage = targetPackage; 7635 mIsSystemRestore = isFullSystemRestore; 7636 mFinished = false; 7637 7638 if (targetPackage != null) { 7639 // Single package restore 7640 mAcceptSet = new ArrayList<PackageInfo>(); 7641 mAcceptSet.add(targetPackage); 7642 } else { 7643 // Everything possible, or a target set 7644 if (filterSet == null) { 7645 // We want everything and a pony 7646 List<PackageInfo> apps = 7647 PackageManagerBackupAgent.getStorableApplications(mPackageManager); 7648 filterSet = packagesToNames(apps); 7649 if (DEBUG) { 7650 Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps"); 7651 } 7652 } 7653 7654 mAcceptSet = new ArrayList<PackageInfo>(filterSet.length); 7655 7656 // Pro tem, we insist on moving the settings provider package to last place. 7657 // Keep track of whether it's in the list, and bump it down if so. We also 7658 // want to do the system package itself first if it's called for. 7659 boolean hasSystem = false; 7660 boolean hasSettings = false; 7661 for (int i = 0; i < filterSet.length; i++) { 7662 try { 7663 PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0); 7664 if ("android".equals(info.packageName)) { 7665 hasSystem = true; 7666 continue; 7667 } 7668 if (SETTINGS_PACKAGE.equals(info.packageName)) { 7669 hasSettings = true; 7670 continue; 7671 } 7672 7673 if (appIsEligibleForBackup(info.applicationInfo)) { 7674 mAcceptSet.add(info); 7675 } 7676 } catch (NameNotFoundException e) { 7677 // requested package name doesn't exist; ignore it 7678 } 7679 } 7680 if (hasSystem) { 7681 try { 7682 mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0)); 7683 } catch (NameNotFoundException e) { 7684 // won't happen; we know a priori that it's valid 7685 } 7686 } 7687 if (hasSettings) { 7688 try { 7689 mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0)); 7690 } catch (NameNotFoundException e) { 7691 // this one is always valid too 7692 } 7693 } 7694 } 7695 7696 if (MORE_DEBUG) { 7697 Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size()); 7698 for (PackageInfo info : mAcceptSet) { 7699 Slog.v(TAG, " " + info.packageName); 7700 } 7701 } 7702 } 7703 7704 private String[] packagesToNames(List<PackageInfo> apps) { 7705 final int N = apps.size(); 7706 String[] names = new String[N]; 7707 for (int i = 0; i < N; i++) { 7708 names[i] = apps.get(i).packageName; 7709 } 7710 return names; 7711 } 7712 7713 // Execute one tick of whatever state machine the task implements 7714 @Override 7715 public void execute() { 7716 if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState); 7717 switch (mState) { 7718 case INITIAL: 7719 startRestore(); 7720 break; 7721 7722 case RUNNING_QUEUE: 7723 dispatchNextRestore(); 7724 break; 7725 7726 case RESTORE_KEYVALUE: 7727 restoreKeyValue(); 7728 break; 7729 7730 case RESTORE_FULL: 7731 restoreFull(); 7732 break; 7733 7734 case RESTORE_FINISHED: 7735 restoreFinished(); 7736 break; 7737 7738 case FINAL: 7739 if (!mFinished) finalizeRestore(); 7740 else { 7741 Slog.e(TAG, "Duplicate finish"); 7742 } 7743 mFinished = true; 7744 break; 7745 } 7746 } 7747 7748 /* 7749 * SKETCH OF OPERATION 7750 * 7751 * create one of these PerformUnifiedRestoreTask objects, telling it which 7752 * dataset & transport to address, and then parameters within the restore 7753 * operation: single target package vs many, etc. 7754 * 7755 * 1. transport.startRestore(token, list-of-packages). If we need @pm@ it is 7756 * always placed first and the settings provider always placed last [for now]. 7757 * 7758 * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline] 7759 * 7760 * [ state change => RUNNING_QUEUE ] 7761 * 7762 * NOW ITERATE: 7763 * 7764 * { 3. t.nextRestorePackage() 7765 * 4. does the metadata for this package allow us to restore it? 7766 * does the on-disk app permit us to restore it? [re-check allowBackup etc] 7767 * 5. is this a key/value dataset? => key/value agent restore 7768 * [ state change => RESTORE_KEYVALUE ] 7769 * 5a. spin up agent 7770 * 5b. t.getRestoreData() to stage it properly 7771 * 5c. call into agent to perform restore 7772 * 5d. tear down agent 7773 * [ state change => RUNNING_QUEUE ] 7774 * 7775 * 6. else it's a stream dataset: 7776 * [ state change => RESTORE_FULL ] 7777 * 6a. instantiate the engine for a stream restore: engine handles agent lifecycles 7778 * 6b. spin off engine runner on separate thread 7779 * 6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket 7780 * [ state change => RUNNING_QUEUE ] 7781 * } 7782 * 7783 * [ state change => FINAL ] 7784 * 7785 * 7. t.finishRestore(), release wakelock, etc. 7786 * 7787 * 7788 */ 7789 7790 // state INITIAL : set up for the restore and read the metadata if necessary 7791 private void startRestore() { 7792 sendStartRestore(mAcceptSet.size()); 7793 7794 // If we're starting a full-system restore, set up to begin widget ID remapping 7795 if (mIsSystemRestore) { 7796 // TODO: http://b/22388012 7797 AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM); 7798 } 7799 7800 try { 7801 String transportDir = mTransport.transportDirName(); 7802 mStateDir = new File(mBaseStateDir, transportDir); 7803 7804 // Fetch the current metadata from the dataset first 7805 PackageInfo pmPackage = new PackageInfo(); 7806 pmPackage.packageName = PACKAGE_MANAGER_SENTINEL; 7807 mAcceptSet.add(0, pmPackage); 7808 7809 PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]); 7810 mStatus = mTransport.startRestore(mToken, packages); 7811 if (mStatus != BackupTransport.TRANSPORT_OK) { 7812 Slog.e(TAG, "Transport error " + mStatus + "; no restore possible"); 7813 mStatus = BackupTransport.TRANSPORT_ERROR; 7814 executeNextState(UnifiedRestoreState.FINAL); 7815 return; 7816 } 7817 7818 RestoreDescription desc = mTransport.nextRestorePackage(); 7819 if (desc == null) { 7820 Slog.e(TAG, "No restore metadata available; halting"); 7821 mStatus = BackupTransport.TRANSPORT_ERROR; 7822 executeNextState(UnifiedRestoreState.FINAL); 7823 return; 7824 } 7825 if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) { 7826 Slog.e(TAG, "Required metadata but got " + desc.getPackageName()); 7827 mStatus = BackupTransport.TRANSPORT_ERROR; 7828 executeNextState(UnifiedRestoreState.FINAL); 7829 return; 7830 } 7831 7832 // Pull the Package Manager metadata from the restore set first 7833 mCurrentPackage = new PackageInfo(); 7834 mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL; 7835 mPmAgent = new PackageManagerBackupAgent(mPackageManager, null); 7836 mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind()); 7837 if (MORE_DEBUG) { 7838 Slog.v(TAG, "initiating restore for PMBA"); 7839 } 7840 initiateOneRestore(mCurrentPackage, 0); 7841 // The PM agent called operationComplete() already, because our invocation 7842 // of it is process-local and therefore synchronous. That means that the 7843 // next-state message (RUNNING_QUEUE) is already enqueued. Only if we're 7844 // unable to proceed with running the queue do we remove that pending 7845 // message and jump straight to the FINAL state. Because this was 7846 // synchronous we also know that we should cancel the pending timeout 7847 // message. 7848 mBackupHandler.removeMessages(MSG_TIMEOUT); 7849 7850 // Verify that the backup set includes metadata. If not, we can't do 7851 // signature/version verification etc, so we simply do not proceed with 7852 // the restore operation. 7853 if (!mPmAgent.hasMetadata()) { 7854 Slog.e(TAG, "No restore metadata available, so not restoring"); 7855 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7856 PACKAGE_MANAGER_SENTINEL, 7857 "Package manager restore metadata missing"); 7858 mStatus = BackupTransport.TRANSPORT_ERROR; 7859 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 7860 executeNextState(UnifiedRestoreState.FINAL); 7861 return; 7862 } 7863 7864 // Success; cache the metadata and continue as expected with the 7865 // next state already enqueued 7866 7867 } catch (RemoteException e) { 7868 // If we lost the transport at any time, halt 7869 Slog.e(TAG, "Unable to contact transport for restore"); 7870 mStatus = BackupTransport.TRANSPORT_ERROR; 7871 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 7872 executeNextState(UnifiedRestoreState.FINAL); 7873 return; 7874 } 7875 } 7876 7877 // state RUNNING_QUEUE : figure out what the next thing to be restored is, 7878 // and fire the appropriate next step 7879 private void dispatchNextRestore() { 7880 UnifiedRestoreState nextState = UnifiedRestoreState.FINAL; 7881 try { 7882 mRestoreDescription = mTransport.nextRestorePackage(); 7883 final String pkgName = (mRestoreDescription != null) 7884 ? mRestoreDescription.getPackageName() : null; 7885 if (pkgName == null) { 7886 Slog.e(TAG, "Failure getting next package name"); 7887 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7888 nextState = UnifiedRestoreState.FINAL; 7889 return; 7890 } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) { 7891 // Yay we've reached the end cleanly 7892 if (DEBUG) { 7893 Slog.v(TAG, "No more packages; finishing restore"); 7894 } 7895 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); 7896 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis); 7897 nextState = UnifiedRestoreState.FINAL; 7898 return; 7899 } 7900 7901 if (DEBUG) { 7902 Slog.i(TAG, "Next restore package: " + mRestoreDescription); 7903 } 7904 sendOnRestorePackage(pkgName); 7905 7906 Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName); 7907 if (metaInfo == null) { 7908 Slog.e(TAG, "No metadata for " + pkgName); 7909 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 7910 "Package metadata missing"); 7911 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7912 return; 7913 } 7914 7915 try { 7916 mCurrentPackage = mPackageManager.getPackageInfo( 7917 pkgName, PackageManager.GET_SIGNATURES); 7918 } catch (NameNotFoundException e) { 7919 // Whoops, we thought we could restore this package but it 7920 // turns out not to be present. Skip it. 7921 Slog.e(TAG, "Package not present: " + pkgName); 7922 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 7923 "Package missing on device"); 7924 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7925 return; 7926 } 7927 7928 if (metaInfo.versionCode > mCurrentPackage.versionCode) { 7929 // Data is from a "newer" version of the app than we have currently 7930 // installed. If the app has not declared that it is prepared to 7931 // handle this case, we do not attempt the restore. 7932 if ((mCurrentPackage.applicationInfo.flags 7933 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { 7934 String message = "Version " + metaInfo.versionCode 7935 + " > installed version " + mCurrentPackage.versionCode; 7936 Slog.w(TAG, "Package " + pkgName + ": " + message); 7937 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 7938 pkgName, message); 7939 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7940 return; 7941 } else { 7942 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode 7943 + " > installed " + mCurrentPackage.versionCode 7944 + " but restoreAnyVersion"); 7945 } 7946 } 7947 7948 if (MORE_DEBUG) Slog.v(TAG, "Package " + pkgName 7949 + " restore version [" + metaInfo.versionCode 7950 + "] is compatible with installed version [" 7951 + mCurrentPackage.versionCode + "]"); 7952 7953 // Reset per-package preconditions and fire the appropriate next state 7954 mWidgetData = null; 7955 final int type = mRestoreDescription.getDataType(); 7956 if (type == RestoreDescription.TYPE_KEY_VALUE) { 7957 nextState = UnifiedRestoreState.RESTORE_KEYVALUE; 7958 } else if (type == RestoreDescription.TYPE_FULL_STREAM) { 7959 nextState = UnifiedRestoreState.RESTORE_FULL; 7960 } else { 7961 // Unknown restore type; ignore this package and move on 7962 Slog.e(TAG, "Unrecognized restore type " + type); 7963 nextState = UnifiedRestoreState.RUNNING_QUEUE; 7964 return; 7965 } 7966 } catch (RemoteException e) { 7967 Slog.e(TAG, "Can't get next target from transport; ending restore"); 7968 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 7969 nextState = UnifiedRestoreState.FINAL; 7970 return; 7971 } finally { 7972 executeNextState(nextState); 7973 } 7974 } 7975 7976 // state RESTORE_KEYVALUE : restore one package via key/value API set 7977 private void restoreKeyValue() { 7978 // Initiating the restore will pass responsibility for the state machine's 7979 // progress to the agent callback, so we do not always execute the 7980 // next state here. 7981 final String packageName = mCurrentPackage.packageName; 7982 // Validate some semantic requirements that apply in this way 7983 // only to the key/value restore API flow 7984 if (mCurrentPackage.applicationInfo.backupAgentName == null 7985 || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) { 7986 if (MORE_DEBUG) { 7987 Slog.i(TAG, "Data exists for package " + packageName 7988 + " but app has no agent; skipping"); 7989 } 7990 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 7991 "Package has no agent"); 7992 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 7993 return; 7994 } 7995 7996 Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); 7997 if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { 7998 Slog.w(TAG, "Signature mismatch restoring " + packageName); 7999 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8000 "Signature mismatch"); 8001 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8002 return; 8003 } 8004 8005 // Good to go! Set up and bind the agent... 8006 mAgent = bindToAgentSynchronous( 8007 mCurrentPackage.applicationInfo, 8008 IApplicationThread.BACKUP_MODE_INCREMENTAL); 8009 if (mAgent == null) { 8010 Slog.w(TAG, "Can't find backup agent for " + packageName); 8011 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8012 "Restore agent missing"); 8013 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8014 return; 8015 } 8016 8017 // And then finally start the restore on this agent 8018 try { 8019 initiateOneRestore(mCurrentPackage, metaInfo.versionCode); 8020 ++mCount; 8021 } catch (Exception e) { 8022 Slog.e(TAG, "Error when attempting restore: " + e.toString()); 8023 keyValueAgentErrorCleanup(); 8024 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8025 } 8026 } 8027 8028 // Guts of a key/value restore operation 8029 void initiateOneRestore(PackageInfo app, int appVersionCode) { 8030 final String packageName = app.packageName; 8031 8032 if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName); 8033 8034 // !!! TODO: get the dirs from the transport 8035 mBackupDataName = new File(mDataDir, packageName + ".restore"); 8036 mStageName = new File(mDataDir, packageName + ".stage"); 8037 mNewStateName = new File(mStateDir, packageName + ".new"); 8038 mSavedStateName = new File(mStateDir, packageName); 8039 8040 // don't stage the 'android' package where the wallpaper data lives. this is 8041 // an optimization: we know there's no widget data hosted/published by that 8042 // package, and this way we avoid doing a spurious copy of MB-sized wallpaper 8043 // data following the download. 8044 boolean staging = !packageName.equals("android"); 8045 ParcelFileDescriptor stage; 8046 File downloadFile = (staging) ? mStageName : mBackupDataName; 8047 8048 final int token = generateToken(); 8049 try { 8050 // Run the transport's restore pass 8051 stage = ParcelFileDescriptor.open(downloadFile, 8052 ParcelFileDescriptor.MODE_READ_WRITE | 8053 ParcelFileDescriptor.MODE_CREATE | 8054 ParcelFileDescriptor.MODE_TRUNCATE); 8055 8056 if (!SELinux.restorecon(mBackupDataName)) { 8057 if (MORE_DEBUG) Slog.e(TAG, "SElinux restorecon failed for " + downloadFile); 8058 } 8059 8060 if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) { 8061 // Transport-level failure, so we wind everything up and 8062 // terminate the restore operation. 8063 Slog.e(TAG, "Error getting restore data for " + packageName); 8064 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8065 stage.close(); 8066 downloadFile.delete(); 8067 executeNextState(UnifiedRestoreState.FINAL); 8068 return; 8069 } 8070 8071 // We have the data from the transport. Now we extract and strip 8072 // any per-package metadata (typically widget-related information) 8073 // if appropriate 8074 if (staging) { 8075 stage.close(); 8076 stage = ParcelFileDescriptor.open(downloadFile, 8077 ParcelFileDescriptor.MODE_READ_ONLY); 8078 8079 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 8080 ParcelFileDescriptor.MODE_READ_WRITE | 8081 ParcelFileDescriptor.MODE_CREATE | 8082 ParcelFileDescriptor.MODE_TRUNCATE); 8083 8084 BackupDataInput in = new BackupDataInput(stage.getFileDescriptor()); 8085 BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor()); 8086 byte[] buffer = new byte[8192]; // will grow when needed 8087 while (in.readNextHeader()) { 8088 final String key = in.getKey(); 8089 final int size = in.getDataSize(); 8090 8091 // is this a special key? 8092 if (key.equals(KEY_WIDGET_STATE)) { 8093 if (DEBUG) { 8094 Slog.i(TAG, "Restoring widget state for " + packageName); 8095 } 8096 mWidgetData = new byte[size]; 8097 in.readEntityData(mWidgetData, 0, size); 8098 } else { 8099 if (size > buffer.length) { 8100 buffer = new byte[size]; 8101 } 8102 in.readEntityData(buffer, 0, size); 8103 out.writeEntityHeader(key, size); 8104 out.writeEntityData(buffer, size); 8105 } 8106 } 8107 8108 mBackupData.close(); 8109 } 8110 8111 // Okay, we have the data. Now have the agent do the restore. 8112 stage.close(); 8113 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 8114 ParcelFileDescriptor.MODE_READ_ONLY); 8115 8116 mNewState = ParcelFileDescriptor.open(mNewStateName, 8117 ParcelFileDescriptor.MODE_READ_WRITE | 8118 ParcelFileDescriptor.MODE_CREATE | 8119 ParcelFileDescriptor.MODE_TRUNCATE); 8120 8121 // Kick off the restore, checking for hung agents. The timeout or 8122 // the operationComplete() callback will schedule the next step, 8123 // so we do not do that here. 8124 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this); 8125 mAgent.doRestore(mBackupData, appVersionCode, mNewState, 8126 token, mBackupManagerBinder); 8127 } catch (Exception e) { 8128 Slog.e(TAG, "Unable to call app for restore: " + packageName, e); 8129 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8130 packageName, e.toString()); 8131 keyValueAgentErrorCleanup(); // clears any pending timeout messages as well 8132 8133 // After a restore failure we go back to running the queue. If there 8134 // are no more packages to be restored that will be handled by the 8135 // next step. 8136 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8137 } 8138 } 8139 8140 // state RESTORE_FULL : restore one package via streaming engine 8141 private void restoreFull() { 8142 // None of this can run on the work looper here, so we spin asynchronous 8143 // work like this: 8144 // 8145 // StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk() 8146 // write it into the pipe to the engine 8147 // EngineThread: FullRestoreEngine thread communicating with the target app 8148 // 8149 // When finished, StreamFeederThread executes next state as appropriate on the 8150 // backup looper, and the overall unified restore task resumes 8151 try { 8152 StreamFeederThread feeder = new StreamFeederThread(); 8153 if (MORE_DEBUG) { 8154 Slog.i(TAG, "Spinning threads for stream restore of " 8155 + mCurrentPackage.packageName); 8156 } 8157 new Thread(feeder, "unified-stream-feeder").start(); 8158 8159 // At this point the feeder is responsible for advancing the restore 8160 // state, so we're done here. 8161 } catch (IOException e) { 8162 // Unable to instantiate the feeder thread -- we need to bail on the 8163 // current target. We haven't asked the transport for data yet, though, 8164 // so we can do that simply by going back to running the restore queue. 8165 Slog.e(TAG, "Unable to construct pipes for stream restore!"); 8166 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8167 } 8168 } 8169 8170 // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end 8171 private void restoreFinished() { 8172 try { 8173 final int token = generateToken(); 8174 prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this); 8175 mAgent.doRestoreFinished(token, mBackupManagerBinder); 8176 // If we get this far, the callback or timeout will schedule the 8177 // next restore state, so we're done 8178 } catch (Exception e) { 8179 final String packageName = mCurrentPackage.packageName; 8180 Slog.e(TAG, "Unable to finalize restore of " + packageName); 8181 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8182 packageName, e.toString()); 8183 keyValueAgentErrorCleanup(); 8184 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8185 } 8186 } 8187 8188 class StreamFeederThread extends RestoreEngine implements Runnable { 8189 final String TAG = "StreamFeederThread"; 8190 FullRestoreEngine mEngine; 8191 8192 // pipe through which we read data from the transport. [0] read, [1] write 8193 ParcelFileDescriptor[] mTransportPipes; 8194 8195 // pipe through which the engine will read data. [0] read, [1] write 8196 ParcelFileDescriptor[] mEnginePipes; 8197 8198 public StreamFeederThread() throws IOException { 8199 mTransportPipes = ParcelFileDescriptor.createPipe(); 8200 mEnginePipes = ParcelFileDescriptor.createPipe(); 8201 setRunning(true); 8202 } 8203 8204 @Override 8205 public void run() { 8206 UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE; 8207 int status = BackupTransport.TRANSPORT_OK; 8208 8209 EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE, 8210 mCurrentPackage.packageName); 8211 8212 mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false); 8213 EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]); 8214 8215 ParcelFileDescriptor eWriteEnd = mEnginePipes[1]; 8216 ParcelFileDescriptor tReadEnd = mTransportPipes[0]; 8217 ParcelFileDescriptor tWriteEnd = mTransportPipes[1]; 8218 8219 int bufferSize = 32 * 1024; 8220 byte[] buffer = new byte[bufferSize]; 8221 FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor()); 8222 FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor()); 8223 8224 // spin up the engine and start moving data to it 8225 new Thread(eThread, "unified-restore-engine").start(); 8226 8227 try { 8228 while (status == BackupTransport.TRANSPORT_OK) { 8229 // have the transport write some of the restoring data to us 8230 int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd); 8231 if (result > 0) { 8232 // The transport wrote this many bytes of restore data to the 8233 // pipe, so pass it along to the engine. 8234 if (MORE_DEBUG) { 8235 Slog.v(TAG, " <- transport provided chunk size " + result); 8236 } 8237 if (result > bufferSize) { 8238 bufferSize = result; 8239 buffer = new byte[bufferSize]; 8240 } 8241 int toCopy = result; 8242 while (toCopy > 0) { 8243 int n = transportIn.read(buffer, 0, toCopy); 8244 engineOut.write(buffer, 0, n); 8245 toCopy -= n; 8246 if (MORE_DEBUG) { 8247 Slog.v(TAG, " -> wrote " + n + " to engine, left=" + toCopy); 8248 } 8249 } 8250 } else if (result == BackupTransport.NO_MORE_DATA) { 8251 // Clean finish. Wind up and we're done! 8252 if (MORE_DEBUG) { 8253 Slog.i(TAG, "Got clean full-restore EOF for " 8254 + mCurrentPackage.packageName); 8255 } 8256 status = BackupTransport.TRANSPORT_OK; 8257 break; 8258 } else { 8259 // Transport reported some sort of failure; the fall-through 8260 // handling will deal properly with that. 8261 Slog.e(TAG, "Error " + result + " streaming restore for " 8262 + mCurrentPackage.packageName); 8263 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8264 status = result; 8265 } 8266 } 8267 if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through"); 8268 } catch (IOException e) { 8269 // We lost our ability to communicate via the pipes. That's worrying 8270 // but potentially recoverable; abandon this package's restore but 8271 // carry on with the next restore target. 8272 Slog.e(TAG, "Unable to route data for restore"); 8273 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8274 mCurrentPackage.packageName, "I/O error on pipes"); 8275 status = BackupTransport.AGENT_ERROR; 8276 } catch (RemoteException e) { 8277 // The transport went away; terminate the whole operation. Closing 8278 // the sockets will wake up the engine and it will then tidy up the 8279 // remote end. 8280 Slog.e(TAG, "Transport failed during restore"); 8281 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8282 status = BackupTransport.TRANSPORT_ERROR; 8283 } finally { 8284 // Close the transport pipes and *our* end of the engine pipe, 8285 // but leave the engine thread's end open so that it properly 8286 // hits EOF and winds up its operations. 8287 IoUtils.closeQuietly(mEnginePipes[1]); 8288 IoUtils.closeQuietly(mTransportPipes[0]); 8289 IoUtils.closeQuietly(mTransportPipes[1]); 8290 8291 // Don't proceed until the engine has finished 8292 eThread.waitForResult(); 8293 8294 if (MORE_DEBUG) { 8295 Slog.i(TAG, "engine thread finished; proceeding"); 8296 } 8297 8298 // Now we're really done with this one too 8299 IoUtils.closeQuietly(mEnginePipes[0]); 8300 8301 // If we hit a transport-level error, we are done with everything; 8302 // if we hit an agent error we just go back to running the queue. 8303 if (status == BackupTransport.TRANSPORT_OK) { 8304 // Clean finish means we issue the restore-finished callback 8305 nextState = UnifiedRestoreState.RESTORE_FINISHED; 8306 8307 // the engine bound the target's agent, so recover that binding 8308 // to use for the callback. 8309 mAgent = mEngine.getAgent(); 8310 } else { 8311 // Something went wrong somewhere. Whether it was at the transport 8312 // level is immaterial; we need to tell the transport to bail 8313 try { 8314 mTransport.abortFullRestore(); 8315 } catch (RemoteException e) { 8316 // transport itself is dead; make sure we handle this as a 8317 // fatal error 8318 status = BackupTransport.TRANSPORT_ERROR; 8319 } 8320 8321 // We also need to wipe the current target's data, as it's probably 8322 // in an incoherent state. 8323 clearApplicationDataSynchronous(mCurrentPackage.packageName); 8324 8325 // Schedule the next state based on the nature of our failure 8326 if (status == BackupTransport.TRANSPORT_ERROR) { 8327 nextState = UnifiedRestoreState.FINAL; 8328 } else { 8329 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8330 } 8331 } 8332 executeNextState(nextState); 8333 setRunning(false); 8334 } 8335 } 8336 8337 } 8338 8339 class EngineThread implements Runnable { 8340 FullRestoreEngine mEngine; 8341 FileInputStream mEngineStream; 8342 8343 EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) { 8344 mEngine = engine; 8345 engine.setRunning(true); 8346 mEngineStream = new FileInputStream(engineSocket.getFileDescriptor()); 8347 } 8348 8349 public boolean isRunning() { 8350 return mEngine.isRunning(); 8351 } 8352 8353 public int waitForResult() { 8354 return mEngine.waitForResult(); 8355 } 8356 8357 @Override 8358 public void run() { 8359 while (mEngine.isRunning()) { 8360 // Tell it to be sure to leave the agent instance up after finishing 8361 mEngine.restoreOneFile(mEngineStream, false); 8362 } 8363 } 8364 } 8365 8366 // state FINAL : tear everything down and we're done. 8367 private void finalizeRestore() { 8368 if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver); 8369 8370 try { 8371 mTransport.finishRestore(); 8372 } catch (Exception e) { 8373 Slog.e(TAG, "Error finishing restore", e); 8374 } 8375 8376 // Tell the observer we're done 8377 if (mObserver != null) { 8378 try { 8379 mObserver.restoreFinished(mStatus); 8380 } catch (RemoteException e) { 8381 Slog.d(TAG, "Restore observer died at restoreFinished"); 8382 } 8383 } 8384 8385 // Clear any ongoing session timeout. 8386 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 8387 8388 // If we have a PM token, we must under all circumstances be sure to 8389 // handshake when we've finished. 8390 if (mPmToken > 0) { 8391 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken); 8392 try { 8393 mPackageManagerBinder.finishPackageInstall(mPmToken); 8394 } catch (RemoteException e) { /* can't happen */ } 8395 } else { 8396 // We were invoked via an active restore session, not by the Package 8397 // Manager, so start up the session timeout again. 8398 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, 8399 TIMEOUT_RESTORE_INTERVAL); 8400 } 8401 8402 // Kick off any work that may be needed regarding app widget restores 8403 // TODO: http://b/22388012 8404 AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM); 8405 8406 // If this was a full-system restore, record the ancestral 8407 // dataset information 8408 if (mIsSystemRestore && mPmAgent != null) { 8409 mAncestralPackages = mPmAgent.getRestoredPackages(); 8410 mAncestralToken = mToken; 8411 writeRestoreTokens(); 8412 } 8413 8414 // done; we can finally release the wakelock and be legitimately done. 8415 Slog.i(TAG, "Restore complete."); 8416 mWakelock.release(); 8417 } 8418 8419 void keyValueAgentErrorCleanup() { 8420 // If the agent fails restore, it might have put the app's data 8421 // into an incoherent state. For consistency we wipe its data 8422 // again in this case before continuing with normal teardown 8423 clearApplicationDataSynchronous(mCurrentPackage.packageName); 8424 keyValueAgentCleanup(); 8425 } 8426 8427 // TODO: clean up naming; this is now used at finish by both k/v and stream restores 8428 void keyValueAgentCleanup() { 8429 mBackupDataName.delete(); 8430 mStageName.delete(); 8431 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 8432 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 8433 mBackupData = mNewState = null; 8434 8435 // if everything went okay, remember the recorded state now 8436 // 8437 // !!! TODO: the restored data could be migrated on the server 8438 // side into the current dataset. In that case the new state file 8439 // we just created would reflect the data already extant in the 8440 // backend, so there'd be nothing more to do. Until that happens, 8441 // however, we need to make sure that we record the data to the 8442 // current backend dataset. (Yes, this means shipping the data over 8443 // the wire in both directions. That's bad, but consistency comes 8444 // first, then efficiency.) Once we introduce server-side data 8445 // migration to the newly-restored device's dataset, we will change 8446 // the following from a discard of the newly-written state to the 8447 // "correct" operation of renaming into the canonical state blob. 8448 mNewStateName.delete(); // TODO: remove; see above comment 8449 //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this 8450 8451 // If this wasn't the PM pseudopackage, tear down the agent side 8452 if (mCurrentPackage.applicationInfo != null) { 8453 // unbind and tidy up even on timeout or failure 8454 try { 8455 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 8456 8457 // The agent was probably running with a stub Application object, 8458 // which isn't a valid run mode for the main app logic. Shut 8459 // down the app so that next time it's launched, it gets the 8460 // usual full initialization. Note that this is only done for 8461 // full-system restores: when a single app has requested a restore, 8462 // it is explicitly not killed following that operation. 8463 // 8464 // We execute this kill when these conditions hold: 8465 // 1. the app did not request its own restore (mTargetPackage == null), and either 8466 // 2a. the app is a full-data target (TYPE_FULL_STREAM) or 8467 // b. the app does not state android:killAfterRestore="false" in its manifest 8468 final int appFlags = mCurrentPackage.applicationInfo.flags; 8469 final boolean killAfterRestore = 8470 (mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM) 8471 || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0); 8472 8473 if (mTargetPackage == null && killAfterRestore) { 8474 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of " 8475 + mCurrentPackage.applicationInfo.processName); 8476 mActivityManager.killApplicationProcess( 8477 mCurrentPackage.applicationInfo.processName, 8478 mCurrentPackage.applicationInfo.uid); 8479 } 8480 } catch (RemoteException e) { 8481 // can't happen; we run in the same process as the activity manager 8482 } 8483 } 8484 8485 // The caller is responsible for reestablishing the state machine; our 8486 // responsibility here is to clear the decks for whatever comes next. 8487 mBackupHandler.removeMessages(MSG_TIMEOUT, this); 8488 synchronized (mCurrentOpLock) { 8489 mCurrentOperations.clear(); 8490 } 8491 } 8492 8493 @Override 8494 public void operationComplete(int unusedResult) { 8495 if (MORE_DEBUG) { 8496 Slog.i(TAG, "operationComplete() during restore: target=" 8497 + mCurrentPackage.packageName 8498 + " state=" + mState); 8499 } 8500 8501 final UnifiedRestoreState nextState; 8502 switch (mState) { 8503 case INITIAL: 8504 // We've just (manually) restored the PMBA. It doesn't need the 8505 // additional restore-finished callback so we bypass that and go 8506 // directly to running the queue. 8507 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8508 break; 8509 8510 case RESTORE_KEYVALUE: 8511 case RESTORE_FULL: { 8512 // Okay, we've just heard back from the agent that it's done with 8513 // the restore itself. We now have to send the same agent its 8514 // doRestoreFinished() callback, so roll into that state. 8515 nextState = UnifiedRestoreState.RESTORE_FINISHED; 8516 break; 8517 } 8518 8519 case RESTORE_FINISHED: { 8520 // Okay, we're done with this package. Tidy up and go on to the next 8521 // app in the queue. 8522 int size = (int) mBackupDataName.length(); 8523 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, 8524 mCurrentPackage.packageName, size); 8525 8526 // Just go back to running the restore queue 8527 keyValueAgentCleanup(); 8528 8529 // If there was widget state associated with this app, get the OS to 8530 // incorporate it into current bookeeping and then pass that along to 8531 // the app as part of the restore-time work. 8532 if (mWidgetData != null) { 8533 restoreWidgetData(mCurrentPackage.packageName, mWidgetData); 8534 } 8535 8536 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8537 break; 8538 } 8539 8540 default: { 8541 // Some kind of horrible semantic error; we're in an unexpected state. 8542 // Back off hard and wind up. 8543 Slog.e(TAG, "Unexpected restore callback into state " + mState); 8544 keyValueAgentErrorCleanup(); 8545 nextState = UnifiedRestoreState.FINAL; 8546 break; 8547 } 8548 } 8549 8550 executeNextState(nextState); 8551 } 8552 8553 // A call to agent.doRestore() or agent.doRestoreFinished() has timed out 8554 @Override 8555 public void handleTimeout() { 8556 Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName); 8557 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8558 mCurrentPackage.packageName, "restore timeout"); 8559 // Handle like an agent that threw on invocation: wipe it and go on to the next 8560 keyValueAgentErrorCleanup(); 8561 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8562 } 8563 8564 void executeNextState(UnifiedRestoreState nextState) { 8565 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 8566 + this + " nextState=" + nextState); 8567 mState = nextState; 8568 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 8569 mBackupHandler.sendMessage(msg); 8570 } 8571 8572 // restore observer support 8573 void sendStartRestore(int numPackages) { 8574 if (mObserver != null) { 8575 try { 8576 mObserver.restoreStarting(numPackages); 8577 } catch (RemoteException e) { 8578 Slog.w(TAG, "Restore observer went away: startRestore"); 8579 mObserver = null; 8580 } 8581 } 8582 } 8583 8584 void sendOnRestorePackage(String name) { 8585 if (mObserver != null) { 8586 if (mObserver != null) { 8587 try { 8588 mObserver.onUpdate(mCount, name); 8589 } catch (RemoteException e) { 8590 Slog.d(TAG, "Restore observer died in onUpdate"); 8591 mObserver = null; 8592 } 8593 } 8594 } 8595 } 8596 8597 void sendEndRestore() { 8598 if (mObserver != null) { 8599 try { 8600 mObserver.restoreFinished(mStatus); 8601 } catch (RemoteException e) { 8602 Slog.w(TAG, "Restore observer went away: endRestore"); 8603 mObserver = null; 8604 } 8605 } 8606 } 8607 } 8608 8609 class PerformClearTask implements Runnable { 8610 IBackupTransport mTransport; 8611 PackageInfo mPackage; 8612 8613 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) { 8614 mTransport = transport; 8615 mPackage = packageInfo; 8616 } 8617 8618 public void run() { 8619 try { 8620 // Clear the on-device backup state to ensure a full backup next time 8621 File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); 8622 File stateFile = new File(stateDir, mPackage.packageName); 8623 stateFile.delete(); 8624 8625 // Tell the transport to remove all the persistent storage for the app 8626 // TODO - need to handle failures 8627 mTransport.clearBackupData(mPackage); 8628 } catch (RemoteException e) { 8629 // can't happen; the transport is local 8630 } catch (Exception e) { 8631 Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage); 8632 } finally { 8633 try { 8634 // TODO - need to handle failures 8635 mTransport.finishBackup(); 8636 } catch (RemoteException e) { 8637 // can't happen; the transport is local 8638 } 8639 8640 // Last but not least, release the cpu 8641 mWakelock.release(); 8642 } 8643 } 8644 } 8645 8646 class PerformInitializeTask implements Runnable { 8647 HashSet<String> mQueue; 8648 8649 PerformInitializeTask(HashSet<String> transportNames) { 8650 mQueue = transportNames; 8651 } 8652 8653 public void run() { 8654 try { 8655 for (String transportName : mQueue) { 8656 IBackupTransport transport = getTransport(transportName); 8657 if (transport == null) { 8658 Slog.e(TAG, "Requested init for " + transportName + " but not found"); 8659 continue; 8660 } 8661 8662 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); 8663 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName()); 8664 long startRealtime = SystemClock.elapsedRealtime(); 8665 int status = transport.initializeDevice(); 8666 8667 if (status == BackupTransport.TRANSPORT_OK) { 8668 status = transport.finishBackup(); 8669 } 8670 8671 // Okay, the wipe really happened. Clean up our local bookkeeping. 8672 if (status == BackupTransport.TRANSPORT_OK) { 8673 Slog.i(TAG, "Device init successful"); 8674 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); 8675 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 8676 resetBackupState(new File(mBaseStateDir, transport.transportDirName())); 8677 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); 8678 synchronized (mQueueLock) { 8679 recordInitPendingLocked(false, transportName); 8680 } 8681 } else { 8682 // If this didn't work, requeue this one and try again 8683 // after a suitable interval 8684 Slog.e(TAG, "Transport error in initializeDevice()"); 8685 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 8686 synchronized (mQueueLock) { 8687 recordInitPendingLocked(true, transportName); 8688 } 8689 // do this via another alarm to make sure of the wakelock states 8690 long delay = transport.requestBackupTime(); 8691 Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay); 8692 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 8693 System.currentTimeMillis() + delay, mRunInitIntent); 8694 } 8695 } 8696 } catch (RemoteException e) { 8697 // can't happen; the transports are local 8698 } catch (Exception e) { 8699 Slog.e(TAG, "Unexpected error performing init", e); 8700 } finally { 8701 // Done; release the wakelock 8702 mWakelock.release(); 8703 } 8704 } 8705 } 8706 8707 private void dataChangedImpl(String packageName) { 8708 HashSet<String> targets = dataChangedTargets(packageName); 8709 dataChangedImpl(packageName, targets); 8710 } 8711 8712 private void dataChangedImpl(String packageName, HashSet<String> targets) { 8713 // Record that we need a backup pass for the caller. Since multiple callers 8714 // may share a uid, we need to note all candidates within that uid and schedule 8715 // a backup pass for each of them. 8716 if (targets == null) { 8717 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 8718 + " uid=" + Binder.getCallingUid()); 8719 return; 8720 } 8721 8722 synchronized (mQueueLock) { 8723 // Note that this client has made data changes that need to be backed up 8724 if (targets.contains(packageName)) { 8725 // Add the caller to the set of pending backups. If there is 8726 // one already there, then overwrite it, but no harm done. 8727 BackupRequest req = new BackupRequest(packageName); 8728 if (mPendingBackups.put(packageName, req) == null) { 8729 if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName); 8730 8731 // Journal this request in case of crash. The put() 8732 // operation returned null when this package was not already 8733 // in the set; we want to avoid touching the disk redundantly. 8734 writeToJournalLocked(packageName); 8735 } 8736 } 8737 } 8738 8739 // ...and schedule a backup pass if necessary 8740 KeyValueBackupJob.schedule(mContext); 8741 } 8742 8743 // Note: packageName is currently unused, but may be in the future 8744 private HashSet<String> dataChangedTargets(String packageName) { 8745 // If the caller does not hold the BACKUP permission, it can only request a 8746 // backup of its own data. 8747 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 8748 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 8749 synchronized (mBackupParticipants) { 8750 return mBackupParticipants.get(Binder.getCallingUid()); 8751 } 8752 } 8753 8754 // a caller with full permission can ask to back up any participating app 8755 HashSet<String> targets = new HashSet<String>(); 8756 if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) { 8757 targets.add(PACKAGE_MANAGER_SENTINEL); 8758 } else { 8759 synchronized (mBackupParticipants) { 8760 int N = mBackupParticipants.size(); 8761 for (int i = 0; i < N; i++) { 8762 HashSet<String> s = mBackupParticipants.valueAt(i); 8763 if (s != null) { 8764 targets.addAll(s); 8765 } 8766 } 8767 } 8768 } 8769 return targets; 8770 } 8771 8772 private void writeToJournalLocked(String str) { 8773 RandomAccessFile out = null; 8774 try { 8775 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir); 8776 out = new RandomAccessFile(mJournal, "rws"); 8777 out.seek(out.length()); 8778 out.writeUTF(str); 8779 } catch (IOException e) { 8780 Slog.e(TAG, "Can't write " + str + " to backup journal", e); 8781 mJournal = null; 8782 } finally { 8783 try { if (out != null) out.close(); } catch (IOException e) {} 8784 } 8785 } 8786 8787 // ----- IBackupManager binder interface ----- 8788 8789 public void dataChanged(final String packageName) { 8790 final int callingUserHandle = UserHandle.getCallingUserId(); 8791 if (callingUserHandle != UserHandle.USER_SYSTEM) { 8792 // TODO: http://b/22388012 8793 // App is running under a non-owner user profile. For now, we do not back 8794 // up data from secondary user profiles. 8795 // TODO: backups for all user profiles although don't add backup for profiles 8796 // without adding admin control in DevicePolicyManager. 8797 if (MORE_DEBUG) { 8798 Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " 8799 + callingUserHandle); 8800 } 8801 return; 8802 } 8803 8804 final HashSet<String> targets = dataChangedTargets(packageName); 8805 if (targets == null) { 8806 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 8807 + " uid=" + Binder.getCallingUid()); 8808 return; 8809 } 8810 8811 mBackupHandler.post(new Runnable() { 8812 public void run() { 8813 dataChangedImpl(packageName, targets); 8814 } 8815 }); 8816 } 8817 8818 // Clear the given package's backup data from the current transport 8819 public void clearBackupData(String transportName, String packageName) { 8820 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); 8821 PackageInfo info; 8822 try { 8823 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 8824 } catch (NameNotFoundException e) { 8825 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); 8826 return; 8827 } 8828 8829 // If the caller does not hold the BACKUP permission, it can only request a 8830 // wipe of its own backed-up data. 8831 HashSet<String> apps; 8832 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 8833 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 8834 apps = mBackupParticipants.get(Binder.getCallingUid()); 8835 } else { 8836 // a caller with full permission can ask to back up any participating app 8837 // !!! TODO: allow data-clear of ANY app? 8838 if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps"); 8839 apps = new HashSet<String>(); 8840 int N = mBackupParticipants.size(); 8841 for (int i = 0; i < N; i++) { 8842 HashSet<String> s = mBackupParticipants.valueAt(i); 8843 if (s != null) { 8844 apps.addAll(s); 8845 } 8846 } 8847 } 8848 8849 // Is the given app an available participant? 8850 if (apps.contains(packageName)) { 8851 // found it; fire off the clear request 8852 if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process"); 8853 mBackupHandler.removeMessages(MSG_RETRY_CLEAR); 8854 synchronized (mQueueLock) { 8855 final IBackupTransport transport = getTransport(transportName); 8856 if (transport == null) { 8857 // transport is currently unavailable -- make sure to retry 8858 Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR, 8859 new ClearRetryParams(transportName, packageName)); 8860 mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL); 8861 return; 8862 } 8863 long oldId = Binder.clearCallingIdentity(); 8864 mWakelock.acquire(); 8865 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, 8866 new ClearParams(transport, info)); 8867 mBackupHandler.sendMessage(msg); 8868 Binder.restoreCallingIdentity(oldId); 8869 } 8870 } 8871 } 8872 8873 // Run a backup pass immediately for any applications that have declared 8874 // that they have pending updates. 8875 public void backupNow() { 8876 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow"); 8877 8878 if (mPowerManager.isPowerSaveMode()) { 8879 if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode"); 8880 KeyValueBackupJob.schedule(mContext); // try again in several hours 8881 } else { 8882 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); 8883 synchronized (mQueueLock) { 8884 // Fire the intent that kicks off the whole shebang... 8885 try { 8886 mRunBackupIntent.send(); 8887 } catch (PendingIntent.CanceledException e) { 8888 // should never happen 8889 Slog.e(TAG, "run-backup intent cancelled!"); 8890 } 8891 8892 // ...and cancel any pending scheduled job, because we've just superseded it 8893 KeyValueBackupJob.cancel(mContext); 8894 } 8895 } 8896 } 8897 8898 boolean deviceIsProvisioned() { 8899 final ContentResolver resolver = mContext.getContentResolver(); 8900 return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); 8901 } 8902 8903 // Run a *full* backup pass for the given packages, writing the resulting data stream 8904 // to the supplied file descriptor. This method is synchronous and does not return 8905 // to the caller until the backup has been completed. 8906 // 8907 // This is the variant used by 'adb backup'; it requires on-screen confirmation 8908 // by the user because it can be used to offload data over untrusted USB. 8909 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, 8910 boolean includeObbs, boolean includeShared, boolean doWidgets, 8911 boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) { 8912 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); 8913 8914 final int callingUserHandle = UserHandle.getCallingUserId(); 8915 // TODO: http://b/22388012 8916 if (callingUserHandle != UserHandle.USER_SYSTEM) { 8917 throw new IllegalStateException("Backup supported only for the device owner"); 8918 } 8919 8920 // Validate 8921 if (!doAllApps) { 8922 if (!includeShared) { 8923 // If we're backing up shared data (sdcard or equivalent), then we can run 8924 // without any supplied app names. Otherwise, we'd be doing no work, so 8925 // report the error. 8926 if (pkgList == null || pkgList.length == 0) { 8927 throw new IllegalArgumentException( 8928 "Backup requested but neither shared nor any apps named"); 8929 } 8930 } 8931 } 8932 8933 long oldId = Binder.clearCallingIdentity(); 8934 try { 8935 // Doesn't make sense to do a full backup prior to setup 8936 if (!deviceIsProvisioned()) { 8937 Slog.i(TAG, "Full backup not supported before setup"); 8938 return; 8939 } 8940 8941 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks 8942 + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps 8943 + " system=" + includeSystem + " pkgs=" + pkgList); 8944 Slog.i(TAG, "Beginning full backup..."); 8945 8946 FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs, 8947 includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList); 8948 final int token = generateToken(); 8949 synchronized (mFullConfirmations) { 8950 mFullConfirmations.put(token, params); 8951 } 8952 8953 // start up the confirmation UI 8954 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token); 8955 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { 8956 Slog.e(TAG, "Unable to launch full backup confirmation"); 8957 mFullConfirmations.delete(token); 8958 return; 8959 } 8960 8961 // make sure the screen is lit for the user interaction 8962 mPowerManager.userActivity(SystemClock.uptimeMillis(), 8963 PowerManager.USER_ACTIVITY_EVENT_OTHER, 8964 0); 8965 8966 // start the confirmation countdown 8967 startConfirmationTimeout(token, params); 8968 8969 // wait for the backup to be performed 8970 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion..."); 8971 waitForCompletion(params); 8972 } finally { 8973 try { 8974 fd.close(); 8975 } catch (IOException e) { 8976 // just eat it 8977 } 8978 Binder.restoreCallingIdentity(oldId); 8979 Slog.d(TAG, "Full backup processing complete."); 8980 } 8981 } 8982 8983 public void fullTransportBackup(String[] pkgNames) { 8984 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, 8985 "fullTransportBackup"); 8986 8987 final int callingUserHandle = UserHandle.getCallingUserId(); 8988 // TODO: http://b/22388012 8989 if (callingUserHandle != UserHandle.USER_SYSTEM) { 8990 throw new IllegalStateException("Restore supported only for the device owner"); 8991 } 8992 8993 if (!fullBackupAllowable(getTransport(mCurrentTransport))) { 8994 Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?"); 8995 } else { 8996 if (DEBUG) { 8997 Slog.d(TAG, "fullTransportBackup()"); 8998 } 8999 9000 CountDownLatch latch = new CountDownLatch(1); 9001 PerformFullTransportBackupTask task = new PerformFullTransportBackupTask(null, pkgNames, 9002 false, null, latch, null, false /* userInitiated */); 9003 // Acquiring wakelock for PerformFullTransportBackupTask before its start. 9004 mWakelock.acquire(); 9005 (new Thread(task, "full-transport-master")).start(); 9006 do { 9007 try { 9008 latch.await(); 9009 break; 9010 } catch (InterruptedException e) { 9011 // Just go back to waiting for the latch to indicate completion 9012 } 9013 } while (true); 9014 9015 // We just ran a backup on these packages, so kick them to the end of the queue 9016 final long now = System.currentTimeMillis(); 9017 for (String pkg : pkgNames) { 9018 enqueueFullBackup(pkg, now); 9019 } 9020 } 9021 9022 if (DEBUG) { 9023 Slog.d(TAG, "Done with full transport backup."); 9024 } 9025 } 9026 9027 public void fullRestore(ParcelFileDescriptor fd) { 9028 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); 9029 9030 final int callingUserHandle = UserHandle.getCallingUserId(); 9031 // TODO: http://b/22388012 9032 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9033 throw new IllegalStateException("Restore supported only for the device owner"); 9034 } 9035 9036 long oldId = Binder.clearCallingIdentity(); 9037 9038 try { 9039 // Check whether the device has been provisioned -- we don't handle 9040 // full restores prior to completing the setup process. 9041 if (!deviceIsProvisioned()) { 9042 Slog.i(TAG, "Full restore not permitted before setup"); 9043 return; 9044 } 9045 9046 Slog.i(TAG, "Beginning full restore..."); 9047 9048 FullRestoreParams params = new FullRestoreParams(fd); 9049 final int token = generateToken(); 9050 synchronized (mFullConfirmations) { 9051 mFullConfirmations.put(token, params); 9052 } 9053 9054 // start up the confirmation UI 9055 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token); 9056 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) { 9057 Slog.e(TAG, "Unable to launch full restore confirmation"); 9058 mFullConfirmations.delete(token); 9059 return; 9060 } 9061 9062 // make sure the screen is lit for the user interaction 9063 mPowerManager.userActivity(SystemClock.uptimeMillis(), 9064 PowerManager.USER_ACTIVITY_EVENT_OTHER, 9065 0); 9066 9067 // start the confirmation countdown 9068 startConfirmationTimeout(token, params); 9069 9070 // wait for the restore to be performed 9071 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion..."); 9072 waitForCompletion(params); 9073 } finally { 9074 try { 9075 fd.close(); 9076 } catch (IOException e) { 9077 Slog.w(TAG, "Error trying to close fd after full restore: " + e); 9078 } 9079 Binder.restoreCallingIdentity(oldId); 9080 Slog.i(TAG, "Full restore processing complete."); 9081 } 9082 } 9083 9084 boolean startConfirmationUi(int token, String action) { 9085 try { 9086 Intent confIntent = new Intent(action); 9087 confIntent.setClassName("com.android.backupconfirm", 9088 "com.android.backupconfirm.BackupRestoreConfirmation"); 9089 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token); 9090 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 9091 mContext.startActivity(confIntent); 9092 } catch (ActivityNotFoundException e) { 9093 return false; 9094 } 9095 return true; 9096 } 9097 9098 void startConfirmationTimeout(int token, FullParams params) { 9099 if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after " 9100 + TIMEOUT_FULL_CONFIRMATION + " millis"); 9101 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, 9102 token, 0, params); 9103 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION); 9104 } 9105 9106 void waitForCompletion(FullParams params) { 9107 synchronized (params.latch) { 9108 while (params.latch.get() == false) { 9109 try { 9110 params.latch.wait(); 9111 } catch (InterruptedException e) { /* never interrupted */ } 9112 } 9113 } 9114 } 9115 9116 void signalFullBackupRestoreCompletion(FullParams params) { 9117 synchronized (params.latch) { 9118 params.latch.set(true); 9119 params.latch.notifyAll(); 9120 } 9121 } 9122 9123 // Confirm that the previously-requested full backup/restore operation can proceed. This 9124 // is used to require a user-facing disclosure about the operation. 9125 public void acknowledgeFullBackupOrRestore(int token, boolean allow, 9126 String curPassword, String encPpassword, IFullBackupRestoreObserver observer) { 9127 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token 9128 + " allow=" + allow); 9129 9130 // TODO: possibly require not just this signature-only permission, but even 9131 // require that the specific designated confirmation-UI app uid is the caller? 9132 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore"); 9133 9134 long oldId = Binder.clearCallingIdentity(); 9135 try { 9136 9137 FullParams params; 9138 synchronized (mFullConfirmations) { 9139 params = mFullConfirmations.get(token); 9140 if (params != null) { 9141 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params); 9142 mFullConfirmations.delete(token); 9143 9144 if (allow) { 9145 final int verb = params instanceof FullBackupParams 9146 ? MSG_RUN_ADB_BACKUP 9147 : MSG_RUN_ADB_RESTORE; 9148 9149 params.observer = observer; 9150 params.curPassword = curPassword; 9151 9152 params.encryptPassword = encPpassword; 9153 9154 if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb); 9155 mWakelock.acquire(); 9156 Message msg = mBackupHandler.obtainMessage(verb, params); 9157 mBackupHandler.sendMessage(msg); 9158 } else { 9159 Slog.w(TAG, "User rejected full backup/restore operation"); 9160 // indicate completion without having actually transferred any data 9161 signalFullBackupRestoreCompletion(params); 9162 } 9163 } else { 9164 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token"); 9165 } 9166 } 9167 } finally { 9168 Binder.restoreCallingIdentity(oldId); 9169 } 9170 } 9171 9172 // Enable/disable backups 9173 public void setBackupEnabled(boolean enable) { 9174 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9175 "setBackupEnabled"); 9176 9177 Slog.i(TAG, "Backup enabled => " + enable); 9178 9179 long oldId = Binder.clearCallingIdentity(); 9180 try { 9181 boolean wasEnabled = mEnabled; 9182 synchronized (this) { 9183 Settings.Secure.putInt(mContext.getContentResolver(), 9184 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0); 9185 mEnabled = enable; 9186 } 9187 9188 synchronized (mQueueLock) { 9189 if (enable && !wasEnabled && mProvisioned) { 9190 // if we've just been enabled, start scheduling backup passes 9191 KeyValueBackupJob.schedule(mContext); 9192 scheduleNextFullBackupJob(0); 9193 } else if (!enable) { 9194 // No longer enabled, so stop running backups 9195 if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup"); 9196 9197 KeyValueBackupJob.cancel(mContext); 9198 9199 // This also constitutes an opt-out, so we wipe any data for 9200 // this device from the backend. We start that process with 9201 // an alarm in order to guarantee wakelock states. 9202 if (wasEnabled && mProvisioned) { 9203 // NOTE: we currently flush every registered transport, not just 9204 // the currently-active one. 9205 HashSet<String> allTransports; 9206 synchronized (mTransports) { 9207 allTransports = new HashSet<String>(mTransports.keySet()); 9208 } 9209 // build the set of transports for which we are posting an init 9210 for (String transport : allTransports) { 9211 recordInitPendingLocked(true, transport); 9212 } 9213 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 9214 mRunInitIntent); 9215 } 9216 } 9217 } 9218 } finally { 9219 Binder.restoreCallingIdentity(oldId); 9220 } 9221 } 9222 9223 // Enable/disable automatic restore of app data at install time 9224 public void setAutoRestore(boolean doAutoRestore) { 9225 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9226 "setAutoRestore"); 9227 9228 Slog.i(TAG, "Auto restore => " + doAutoRestore); 9229 9230 final long oldId = Binder.clearCallingIdentity(); 9231 try { 9232 synchronized (this) { 9233 Settings.Secure.putInt(mContext.getContentResolver(), 9234 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0); 9235 mAutoRestore = doAutoRestore; 9236 } 9237 } finally { 9238 Binder.restoreCallingIdentity(oldId); 9239 } 9240 } 9241 9242 // Mark the backup service as having been provisioned 9243 public void setBackupProvisioned(boolean available) { 9244 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9245 "setBackupProvisioned"); 9246 /* 9247 * This is now a no-op; provisioning is simply the device's own setup state. 9248 */ 9249 } 9250 9251 // Report whether the backup mechanism is currently enabled 9252 public boolean isBackupEnabled() { 9253 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); 9254 return mEnabled; // no need to synchronize just to read it 9255 } 9256 9257 // Report the name of the currently active transport 9258 public String getCurrentTransport() { 9259 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9260 "getCurrentTransport"); 9261 if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); 9262 return mCurrentTransport; 9263 } 9264 9265 // Report all known, available backup transports 9266 public String[] listAllTransports() { 9267 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); 9268 9269 String[] list = null; 9270 ArrayList<String> known = new ArrayList<String>(); 9271 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { 9272 if (entry.getValue() != null) { 9273 known.add(entry.getKey()); 9274 } 9275 } 9276 9277 if (known.size() > 0) { 9278 list = new String[known.size()]; 9279 known.toArray(list); 9280 } 9281 return list; 9282 } 9283 9284 // Select which transport to use for the next backup operation. 9285 public String selectBackupTransport(String transport) { 9286 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9287 "selectBackupTransport"); 9288 9289 synchronized (mTransports) { 9290 final long oldId = Binder.clearCallingIdentity(); 9291 try { 9292 String prevTransport = mCurrentTransport; 9293 mCurrentTransport = transport; 9294 Settings.Secure.putString(mContext.getContentResolver(), 9295 Settings.Secure.BACKUP_TRANSPORT, transport); 9296 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport 9297 + " returning " + prevTransport); 9298 return prevTransport; 9299 } finally { 9300 Binder.restoreCallingIdentity(oldId); 9301 } 9302 } 9303 } 9304 9305 // Supply the configuration Intent for the given transport. If the name is not one 9306 // of the available transports, or if the transport does not supply any configuration 9307 // UI, the method returns null. 9308 public Intent getConfigurationIntent(String transportName) { 9309 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9310 "getConfigurationIntent"); 9311 9312 synchronized (mTransports) { 9313 final IBackupTransport transport = mTransports.get(transportName); 9314 if (transport != null) { 9315 try { 9316 final Intent intent = transport.configurationIntent(); 9317 if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent " 9318 + intent); 9319 return intent; 9320 } catch (RemoteException e) { 9321 /* fall through to return null */ 9322 } 9323 } 9324 } 9325 9326 return null; 9327 } 9328 9329 // Supply the configuration summary string for the given transport. If the name is 9330 // not one of the available transports, or if the transport does not supply any 9331 // summary / destination string, the method can return null. 9332 // 9333 // This string is used VERBATIM as the summary text of the relevant Settings item! 9334 public String getDestinationString(String transportName) { 9335 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9336 "getDestinationString"); 9337 9338 synchronized (mTransports) { 9339 final IBackupTransport transport = mTransports.get(transportName); 9340 if (transport != null) { 9341 try { 9342 final String text = transport.currentDestinationString(); 9343 if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text); 9344 return text; 9345 } catch (RemoteException e) { 9346 /* fall through to return null */ 9347 } 9348 } 9349 } 9350 9351 return null; 9352 } 9353 9354 // Supply the manage-data intent for the given transport. 9355 public Intent getDataManagementIntent(String transportName) { 9356 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9357 "getDataManagementIntent"); 9358 9359 synchronized (mTransports) { 9360 final IBackupTransport transport = mTransports.get(transportName); 9361 if (transport != null) { 9362 try { 9363 final Intent intent = transport.dataManagementIntent(); 9364 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent " 9365 + intent); 9366 return intent; 9367 } catch (RemoteException e) { 9368 /* fall through to return null */ 9369 } 9370 } 9371 } 9372 9373 return null; 9374 } 9375 9376 // Supply the menu label for affordances that fire the manage-data intent 9377 // for the given transport. 9378 public String getDataManagementLabel(String transportName) { 9379 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9380 "getDataManagementLabel"); 9381 9382 synchronized (mTransports) { 9383 final IBackupTransport transport = mTransports.get(transportName); 9384 if (transport != null) { 9385 try { 9386 final String text = transport.dataManagementLabel(); 9387 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text); 9388 return text; 9389 } catch (RemoteException e) { 9390 /* fall through to return null */ 9391 } 9392 } 9393 } 9394 9395 return null; 9396 } 9397 9398 // Callback: a requested backup agent has been instantiated. This should only 9399 // be called from the Activity Manager. 9400 public void agentConnected(String packageName, IBinder agentBinder) { 9401 synchronized(mAgentConnectLock) { 9402 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 9403 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); 9404 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); 9405 mConnectedAgent = agent; 9406 mConnecting = false; 9407 } else { 9408 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9409 + " claiming agent connected"); 9410 } 9411 mAgentConnectLock.notifyAll(); 9412 } 9413 } 9414 9415 // Callback: a backup agent has failed to come up, or has unexpectedly quit. 9416 // If the agent failed to come up in the first place, the agentBinder argument 9417 // will be null. This should only be called from the Activity Manager. 9418 public void agentDisconnected(String packageName) { 9419 // TODO: handle backup being interrupted 9420 synchronized(mAgentConnectLock) { 9421 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 9422 mConnectedAgent = null; 9423 mConnecting = false; 9424 } else { 9425 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9426 + " claiming agent disconnected"); 9427 } 9428 mAgentConnectLock.notifyAll(); 9429 } 9430 } 9431 9432 // An application being installed will need a restore pass, then the Package Manager 9433 // will need to be told when the restore is finished. 9434 public void restoreAtInstall(String packageName, int token) { 9435 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 9436 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9437 + " attemping install-time restore"); 9438 return; 9439 } 9440 9441 boolean skip = false; 9442 9443 long restoreSet = getAvailableRestoreToken(packageName); 9444 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName 9445 + " token=" + Integer.toHexString(token) 9446 + " restoreSet=" + Long.toHexString(restoreSet)); 9447 if (restoreSet == 0) { 9448 if (MORE_DEBUG) Slog.i(TAG, "No restore set"); 9449 skip = true; 9450 } 9451 9452 // Do we have a transport to fetch data for us? 9453 IBackupTransport transport = getTransport(mCurrentTransport); 9454 if (transport == null) { 9455 if (DEBUG) Slog.w(TAG, "No transport"); 9456 skip = true; 9457 } 9458 9459 if (!mAutoRestore) { 9460 if (DEBUG) { 9461 Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore); 9462 } 9463 skip = true; 9464 } 9465 9466 if (!skip) { 9467 try { 9468 // okay, we're going to attempt a restore of this package from this restore set. 9469 // The eventual message back into the Package Manager to run the post-install 9470 // steps for 'token' will be issued from the restore handling code. 9471 9472 // This can throw and so *must* happen before the wakelock is acquired 9473 String dirName = transport.transportDirName(); 9474 9475 mWakelock.acquire(); 9476 if (MORE_DEBUG) { 9477 Slog.d(TAG, "Restore at install of " + packageName); 9478 } 9479 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9480 msg.obj = new RestoreParams(transport, dirName, null, 9481 restoreSet, packageName, token); 9482 mBackupHandler.sendMessage(msg); 9483 } catch (RemoteException e) { 9484 // Binding to the transport broke; back off and proceed with the installation. 9485 Slog.e(TAG, "Unable to contact transport"); 9486 skip = true; 9487 } 9488 } 9489 9490 if (skip) { 9491 // Auto-restore disabled or no way to attempt a restore; just tell the Package 9492 // Manager to proceed with the post-install handling for this package. 9493 if (DEBUG) Slog.v(TAG, "Finishing install immediately"); 9494 try { 9495 mPackageManagerBinder.finishPackageInstall(token); 9496 } catch (RemoteException e) { /* can't happen */ } 9497 } 9498 } 9499 9500 // Hand off a restore session 9501 public IRestoreSession beginRestoreSession(String packageName, String transport) { 9502 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName 9503 + " transport=" + transport); 9504 9505 boolean needPermission = true; 9506 if (transport == null) { 9507 transport = mCurrentTransport; 9508 9509 if (packageName != null) { 9510 PackageInfo app = null; 9511 try { 9512 app = mPackageManager.getPackageInfo(packageName, 0); 9513 } catch (NameNotFoundException nnf) { 9514 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 9515 throw new IllegalArgumentException("Package " + packageName + " not found"); 9516 } 9517 9518 if (app.applicationInfo.uid == Binder.getCallingUid()) { 9519 // So: using the current active transport, and the caller has asked 9520 // that its own package will be restored. In this narrow use case 9521 // we do not require the caller to hold the permission. 9522 needPermission = false; 9523 } 9524 } 9525 } 9526 9527 if (needPermission) { 9528 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9529 "beginRestoreSession"); 9530 } else { 9531 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed"); 9532 } 9533 9534 synchronized(this) { 9535 if (mActiveRestoreSession != null) { 9536 Slog.d(TAG, "Restore session requested but one already active"); 9537 return null; 9538 } 9539 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport); 9540 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 9541 } 9542 return mActiveRestoreSession; 9543 } 9544 9545 void clearRestoreSession(ActiveRestoreSession currentSession) { 9546 synchronized(this) { 9547 if (currentSession != mActiveRestoreSession) { 9548 Slog.e(TAG, "ending non-current restore session"); 9549 } else { 9550 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout"); 9551 mActiveRestoreSession = null; 9552 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9553 } 9554 } 9555 } 9556 9557 // Note that a currently-active backup agent has notified us that it has 9558 // completed the given outstanding asynchronous backup/restore operation. 9559 public void opComplete(int token, long result) { 9560 if (MORE_DEBUG) { 9561 Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result); 9562 } 9563 Operation op = null; 9564 synchronized (mCurrentOpLock) { 9565 op = mCurrentOperations.get(token); 9566 if (op != null) { 9567 op.state = OP_ACKNOWLEDGED; 9568 } 9569 mCurrentOpLock.notifyAll(); 9570 } 9571 9572 // The completion callback, if any, is invoked on the handler 9573 if (op != null && op.callback != null) { 9574 Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback); 9575 // NB: this cannot distinguish between results > 2 gig 9576 msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result; 9577 mBackupHandler.sendMessage(msg); 9578 } 9579 } 9580 9581 public boolean isAppEligibleForBackup(String packageName) { 9582 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9583 "isAppEligibleForBackup"); 9584 try { 9585 PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 9586 PackageManager.GET_SIGNATURES); 9587 if (!appIsEligibleForBackup(packageInfo.applicationInfo) || 9588 appIsStopped(packageInfo.applicationInfo)) { 9589 return false; 9590 } 9591 IBackupTransport transport = getTransport(mCurrentTransport); 9592 if (transport != null) { 9593 try { 9594 return transport.isAppEligibleForBackup(packageInfo, 9595 appGetsFullBackup(packageInfo)); 9596 } catch (RemoteException e) { 9597 Slog.e(TAG, "Unable to contact transport"); 9598 } 9599 } 9600 // If transport is not present we couldn't tell that the package is not eligible. 9601 return true; 9602 } catch (NameNotFoundException e) { 9603 return false; 9604 } 9605 } 9606 9607 // ----- Restore session ----- 9608 9609 class ActiveRestoreSession extends IRestoreSession.Stub { 9610 private static final String TAG = "RestoreSession"; 9611 9612 private String mPackageName; 9613 private IBackupTransport mRestoreTransport = null; 9614 RestoreSet[] mRestoreSets = null; 9615 boolean mEnded = false; 9616 boolean mTimedOut = false; 9617 9618 ActiveRestoreSession(String packageName, String transport) { 9619 mPackageName = packageName; 9620 mRestoreTransport = getTransport(transport); 9621 } 9622 9623 public void markTimedOut() { 9624 mTimedOut = true; 9625 } 9626 9627 // --- Binder interface --- 9628 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) { 9629 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9630 "getAvailableRestoreSets"); 9631 if (observer == null) { 9632 throw new IllegalArgumentException("Observer must not be null"); 9633 } 9634 9635 if (mEnded) { 9636 throw new IllegalStateException("Restore session already ended"); 9637 } 9638 9639 if (mTimedOut) { 9640 Slog.i(TAG, "Session already timed out"); 9641 return -1; 9642 } 9643 9644 long oldId = Binder.clearCallingIdentity(); 9645 try { 9646 if (mRestoreTransport == null) { 9647 Slog.w(TAG, "Null transport getting restore sets"); 9648 return -1; 9649 } 9650 9651 // We know we're doing legit work now, so halt the timeout 9652 // until we're done. It gets started again when the result 9653 // comes in. 9654 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9655 9656 // spin off the transport request to our service thread 9657 mWakelock.acquire(); 9658 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS, 9659 new RestoreGetSetsParams(mRestoreTransport, this, observer)); 9660 mBackupHandler.sendMessage(msg); 9661 return 0; 9662 } catch (Exception e) { 9663 Slog.e(TAG, "Error in getAvailableRestoreSets", e); 9664 return -1; 9665 } finally { 9666 Binder.restoreCallingIdentity(oldId); 9667 } 9668 } 9669 9670 public synchronized int restoreAll(long token, IRestoreObserver observer) { 9671 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9672 "performRestore"); 9673 9674 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token) 9675 + " observer=" + observer); 9676 9677 if (mEnded) { 9678 throw new IllegalStateException("Restore session already ended"); 9679 } 9680 9681 if (mTimedOut) { 9682 Slog.i(TAG, "Session already timed out"); 9683 return -1; 9684 } 9685 9686 if (mRestoreTransport == null || mRestoreSets == null) { 9687 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 9688 return -1; 9689 } 9690 9691 if (mPackageName != null) { 9692 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 9693 return -1; 9694 } 9695 9696 String dirName; 9697 try { 9698 dirName = mRestoreTransport.transportDirName(); 9699 } catch (RemoteException e) { 9700 // Transport went AWOL; fail. 9701 Slog.e(TAG, "Unable to contact transport for restore"); 9702 return -1; 9703 } 9704 9705 synchronized (mQueueLock) { 9706 for (int i = 0; i < mRestoreSets.length; i++) { 9707 if (token == mRestoreSets[i].token) { 9708 // Real work, so stop the session timeout until we finalize the restore 9709 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9710 9711 long oldId = Binder.clearCallingIdentity(); 9712 mWakelock.acquire(); 9713 if (MORE_DEBUG) { 9714 Slog.d(TAG, "restoreAll() kicking off"); 9715 } 9716 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9717 msg.obj = new RestoreParams(mRestoreTransport, dirName, 9718 observer, token); 9719 mBackupHandler.sendMessage(msg); 9720 Binder.restoreCallingIdentity(oldId); 9721 return 0; 9722 } 9723 } 9724 } 9725 9726 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 9727 return -1; 9728 } 9729 9730 // Restores of more than a single package are treated as 'system' restores 9731 public synchronized int restoreSome(long token, IRestoreObserver observer, 9732 String[] packages) { 9733 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9734 "performRestore"); 9735 9736 if (DEBUG) { 9737 StringBuilder b = new StringBuilder(128); 9738 b.append("restoreSome token="); 9739 b.append(Long.toHexString(token)); 9740 b.append(" observer="); 9741 b.append(observer.toString()); 9742 b.append(" packages="); 9743 if (packages == null) { 9744 b.append("null"); 9745 } else { 9746 b.append('{'); 9747 boolean first = true; 9748 for (String s : packages) { 9749 if (!first) { 9750 b.append(", "); 9751 } else first = false; 9752 b.append(s); 9753 } 9754 b.append('}'); 9755 } 9756 Slog.d(TAG, b.toString()); 9757 } 9758 9759 if (mEnded) { 9760 throw new IllegalStateException("Restore session already ended"); 9761 } 9762 9763 if (mTimedOut) { 9764 Slog.i(TAG, "Session already timed out"); 9765 return -1; 9766 } 9767 9768 if (mRestoreTransport == null || mRestoreSets == null) { 9769 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 9770 return -1; 9771 } 9772 9773 if (mPackageName != null) { 9774 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 9775 return -1; 9776 } 9777 9778 String dirName; 9779 try { 9780 dirName = mRestoreTransport.transportDirName(); 9781 } catch (RemoteException e) { 9782 // Transport went AWOL; fail. 9783 Slog.e(TAG, "Unable to contact transport for restore"); 9784 return -1; 9785 } 9786 9787 synchronized (mQueueLock) { 9788 for (int i = 0; i < mRestoreSets.length; i++) { 9789 if (token == mRestoreSets[i].token) { 9790 // Stop the session timeout until we finalize the restore 9791 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9792 9793 long oldId = Binder.clearCallingIdentity(); 9794 mWakelock.acquire(); 9795 if (MORE_DEBUG) { 9796 Slog.d(TAG, "restoreSome() of " + packages.length + " packages"); 9797 } 9798 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9799 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, 9800 packages, packages.length > 1); 9801 mBackupHandler.sendMessage(msg); 9802 Binder.restoreCallingIdentity(oldId); 9803 return 0; 9804 } 9805 } 9806 } 9807 9808 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 9809 return -1; 9810 } 9811 9812 public synchronized int restorePackage(String packageName, IRestoreObserver observer) { 9813 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); 9814 9815 if (mEnded) { 9816 throw new IllegalStateException("Restore session already ended"); 9817 } 9818 9819 if (mTimedOut) { 9820 Slog.i(TAG, "Session already timed out"); 9821 return -1; 9822 } 9823 9824 if (mPackageName != null) { 9825 if (! mPackageName.equals(packageName)) { 9826 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName 9827 + " on session for package " + mPackageName); 9828 return -1; 9829 } 9830 } 9831 9832 PackageInfo app = null; 9833 try { 9834 app = mPackageManager.getPackageInfo(packageName, 0); 9835 } catch (NameNotFoundException nnf) { 9836 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 9837 return -1; 9838 } 9839 9840 // If the caller is not privileged and is not coming from the target 9841 // app's uid, throw a permission exception back to the caller. 9842 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP, 9843 Binder.getCallingPid(), Binder.getCallingUid()); 9844 if ((perm == PackageManager.PERMISSION_DENIED) && 9845 (app.applicationInfo.uid != Binder.getCallingUid())) { 9846 Slog.w(TAG, "restorePackage: bad packageName=" + packageName 9847 + " or calling uid=" + Binder.getCallingUid()); 9848 throw new SecurityException("No permission to restore other packages"); 9849 } 9850 9851 // So far so good; we're allowed to try to restore this package. 9852 long oldId = Binder.clearCallingIdentity(); 9853 try { 9854 // Check whether there is data for it in the current dataset, falling back 9855 // to the ancestral dataset if not. 9856 long token = getAvailableRestoreToken(packageName); 9857 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName 9858 + " token=" + Long.toHexString(token)); 9859 9860 // If we didn't come up with a place to look -- no ancestral dataset and 9861 // the app has never been backed up from this device -- there's nothing 9862 // to do but return failure. 9863 if (token == 0) { 9864 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring"); 9865 return -1; 9866 } 9867 9868 String dirName; 9869 try { 9870 dirName = mRestoreTransport.transportDirName(); 9871 } catch (RemoteException e) { 9872 // Transport went AWOL; fail. 9873 Slog.e(TAG, "Unable to contact transport for restore"); 9874 return -1; 9875 } 9876 9877 // Stop the session timeout until we finalize the restore 9878 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 9879 9880 // Ready to go: enqueue the restore request and claim success 9881 mWakelock.acquire(); 9882 if (MORE_DEBUG) { 9883 Slog.d(TAG, "restorePackage() : " + packageName); 9884 } 9885 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9886 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app); 9887 mBackupHandler.sendMessage(msg); 9888 } finally { 9889 Binder.restoreCallingIdentity(oldId); 9890 } 9891 return 0; 9892 } 9893 9894 // Posted to the handler to tear down a restore session in a cleanly synchronized way 9895 class EndRestoreRunnable implements Runnable { 9896 BackupManagerService mBackupManager; 9897 ActiveRestoreSession mSession; 9898 9899 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) { 9900 mBackupManager = manager; 9901 mSession = session; 9902 } 9903 9904 public void run() { 9905 // clean up the session's bookkeeping 9906 synchronized (mSession) { 9907 mSession.mRestoreTransport = null; 9908 mSession.mEnded = true; 9909 } 9910 9911 // clean up the BackupManagerImpl side of the bookkeeping 9912 // and cancel any pending timeout message 9913 mBackupManager.clearRestoreSession(mSession); 9914 } 9915 } 9916 9917 public synchronized void endRestoreSession() { 9918 if (DEBUG) Slog.d(TAG, "endRestoreSession"); 9919 9920 if (mTimedOut) { 9921 Slog.i(TAG, "Session already timed out"); 9922 return; 9923 } 9924 9925 if (mEnded) { 9926 throw new IllegalStateException("Restore session already ended"); 9927 } 9928 9929 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this)); 9930 } 9931 } 9932 9933 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 9934 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 9935 9936 long identityToken = Binder.clearCallingIdentity(); 9937 try { 9938 if (args != null) { 9939 for (String arg : args) { 9940 if ("-h".equals(arg)) { 9941 pw.println("'dumpsys backup' optional arguments:"); 9942 pw.println(" -h : this help text"); 9943 pw.println(" a[gents] : dump information about defined backup agents"); 9944 return; 9945 } else if ("agents".startsWith(arg)) { 9946 dumpAgents(pw); 9947 return; 9948 } 9949 } 9950 } 9951 dumpInternal(pw); 9952 } finally { 9953 Binder.restoreCallingIdentity(identityToken); 9954 } 9955 } 9956 9957 private void dumpAgents(PrintWriter pw) { 9958 List<PackageInfo> agentPackages = allAgentPackages(); 9959 pw.println("Defined backup agents:"); 9960 for (PackageInfo pkg : agentPackages) { 9961 pw.print(" "); 9962 pw.print(pkg.packageName); pw.println(':'); 9963 pw.print(" "); pw.println(pkg.applicationInfo.backupAgentName); 9964 } 9965 } 9966 9967 private void dumpInternal(PrintWriter pw) { 9968 synchronized (mQueueLock) { 9969 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") 9970 + " / " + (!mProvisioned ? "not " : "") + "provisioned / " 9971 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); 9972 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); 9973 if (mBackupRunning) pw.println("Backup currently running"); 9974 pw.println("Last backup pass started: " + mLastBackupPass 9975 + " (now = " + System.currentTimeMillis() + ')'); 9976 pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled()); 9977 9978 pw.println("Available transports:"); 9979 final String[] transports = listAllTransports(); 9980 if (transports != null) { 9981 for (String t : listAllTransports()) { 9982 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t); 9983 try { 9984 IBackupTransport transport = getTransport(t); 9985 File dir = new File(mBaseStateDir, transport.transportDirName()); 9986 pw.println(" destination: " + transport.currentDestinationString()); 9987 pw.println(" intent: " + transport.configurationIntent()); 9988 for (File f : dir.listFiles()) { 9989 pw.println(" " + f.getName() + " - " + f.length() + " state bytes"); 9990 } 9991 } catch (Exception e) { 9992 Slog.e(TAG, "Error in transport", e); 9993 pw.println(" Error: " + e); 9994 } 9995 } 9996 } 9997 9998 pw.println("Pending init: " + mPendingInits.size()); 9999 for (String s : mPendingInits) { 10000 pw.println(" " + s); 10001 } 10002 10003 if (DEBUG_BACKUP_TRACE) { 10004 synchronized (mBackupTrace) { 10005 if (!mBackupTrace.isEmpty()) { 10006 pw.println("Most recent backup trace:"); 10007 for (String s : mBackupTrace) { 10008 pw.println(" " + s); 10009 } 10010 } 10011 } 10012 } 10013 10014 pw.print("Ancestral: "); pw.println(Long.toHexString(mAncestralToken)); 10015 pw.print("Current: "); pw.println(Long.toHexString(mCurrentToken)); 10016 10017 int N = mBackupParticipants.size(); 10018 pw.println("Participants:"); 10019 for (int i=0; i<N; i++) { 10020 int uid = mBackupParticipants.keyAt(i); 10021 pw.print(" uid: "); 10022 pw.println(uid); 10023 HashSet<String> participants = mBackupParticipants.valueAt(i); 10024 for (String app: participants) { 10025 pw.println(" " + app); 10026 } 10027 } 10028 10029 pw.println("Ancestral packages: " 10030 + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); 10031 if (mAncestralPackages != null) { 10032 for (String pkg : mAncestralPackages) { 10033 pw.println(" " + pkg); 10034 } 10035 } 10036 10037 pw.println("Ever backed up: " + mEverStoredApps.size()); 10038 for (String pkg : mEverStoredApps) { 10039 pw.println(" " + pkg); 10040 } 10041 10042 pw.println("Pending key/value backup: " + mPendingBackups.size()); 10043 for (BackupRequest req : mPendingBackups.values()) { 10044 pw.println(" " + req); 10045 } 10046 10047 pw.println("Full backup queue:" + mFullBackupQueue.size()); 10048 for (FullBackupEntry entry : mFullBackupQueue) { 10049 pw.print(" "); pw.print(entry.lastBackup); 10050 pw.print(" : "); pw.println(entry.packageName); 10051 } 10052 } 10053 } 10054 10055 private static void sendBackupOnUpdate(IBackupObserver observer, String packageName, 10056 BackupProgress progress) { 10057 if (observer != null) { 10058 try { 10059 observer.onUpdate(packageName, progress); 10060 } catch (RemoteException e) { 10061 if (DEBUG) { 10062 Slog.w(TAG, "Backup observer went away: onUpdate"); 10063 } 10064 } 10065 } 10066 } 10067 10068 private static void sendBackupOnResult(IBackupObserver observer, String packageName, 10069 int status) { 10070 if (observer != null) { 10071 try { 10072 observer.onResult(packageName, status); 10073 } catch (RemoteException e) { 10074 if (DEBUG) { 10075 Slog.w(TAG, "Backup observer went away: onResult"); 10076 } 10077 } 10078 } 10079 } 10080 10081 private static void sendBackupFinished(IBackupObserver observer, int status) { 10082 if (observer != null) { 10083 try { 10084 observer.backupFinished(status); 10085 } catch (RemoteException e) { 10086 if (DEBUG) { 10087 Slog.w(TAG, "Backup observer went away: backupFinished"); 10088 } 10089 } 10090 } 10091 } 10092} 10093