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