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