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.versionCode >= version) { 6061 Slog.i(TAG, "Sig + version match; taking data"); 6062 policy = RestorePolicy.ACCEPT; 6063 } else { 6064 // The data is from a newer version of the app than 6065 // is presently installed. That means we can only 6066 // use it if the matching apk is also supplied. 6067 if (mAllowApks) { 6068 Slog.i(TAG, "Data version " + version 6069 + " is newer than installed version " 6070 + pkgInfo.versionCode 6071 + " - requiring apk"); 6072 policy = RestorePolicy.ACCEPT_IF_APK; 6073 } else { 6074 Slog.i(TAG, "Data requires newer version " 6075 + version + "; ignoring"); 6076 policy = RestorePolicy.IGNORE; 6077 } 6078 } 6079 } else { 6080 Slog.w(TAG, "Restore manifest signatures do not match " 6081 + "installed application for " + info.packageName); 6082 } 6083 } else { 6084 Slog.w(TAG, "Package " + info.packageName 6085 + " is system level with no agent"); 6086 } 6087 } else { 6088 if (DEBUG) Slog.i(TAG, "Restore manifest from " 6089 + info.packageName + " but allowBackup=false"); 6090 } 6091 } catch (NameNotFoundException e) { 6092 // Okay, the target app isn't installed. We can process 6093 // the restore properly only if the dataset provides the 6094 // apk file and we can successfully install it. 6095 if (mAllowApks) { 6096 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 6097 + " not installed; requiring apk in dataset"); 6098 policy = RestorePolicy.ACCEPT_IF_APK; 6099 } else { 6100 policy = RestorePolicy.IGNORE; 6101 } 6102 } 6103 6104 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 6105 Slog.i(TAG, "Cannot restore package " + info.packageName 6106 + " without the matching .apk"); 6107 } 6108 } else { 6109 Slog.i(TAG, "Missing signature on backed-up package " 6110 + info.packageName); 6111 } 6112 } else { 6113 Slog.i(TAG, "Expected package " + info.packageName 6114 + " but restore manifest claims " + manifestPackage); 6115 } 6116 } else { 6117 Slog.i(TAG, "Unknown restore manifest version " + version 6118 + " for package " + info.packageName); 6119 } 6120 } catch (NumberFormatException e) { 6121 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 6122 } catch (IllegalArgumentException e) { 6123 Slog.w(TAG, e.getMessage()); 6124 } 6125 6126 return policy; 6127 } 6128 6129 // Builds a line from a byte buffer starting at 'offset', and returns 6130 // the index of the next unconsumed data in the buffer. 6131 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 6132 final int end = buffer.length; 6133 if (offset >= end) throw new IOException("Incomplete data"); 6134 6135 int pos; 6136 for (pos = offset; pos < end; pos++) { 6137 byte c = buffer[pos]; 6138 // at LF we declare end of line, and return the next char as the 6139 // starting point for the next time through 6140 if (c == '\n') { 6141 break; 6142 } 6143 } 6144 outStr[0] = new String(buffer, offset, pos - offset); 6145 pos++; // may be pointing an extra byte past the end but that's okay 6146 return pos; 6147 } 6148 6149 void dumpFileMetadata(FileMetadata info) { 6150 if (MORE_DEBUG) { 6151 StringBuilder b = new StringBuilder(128); 6152 6153 // mode string 6154 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 6155 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 6156 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 6157 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 6158 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 6159 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 6160 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 6161 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 6162 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 6163 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 6164 b.append(String.format(" %9d ", info.size)); 6165 6166 Date stamp = new Date(info.mtime); 6167 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 6168 6169 b.append(info.packageName); 6170 b.append(" :: "); 6171 b.append(info.domain); 6172 b.append(" :: "); 6173 b.append(info.path); 6174 6175 Slog.i(TAG, b.toString()); 6176 } 6177 } 6178 6179 // Consume a tar file header block [sequence] and accumulate the relevant metadata 6180 FileMetadata readTarHeaders(InputStream instream) throws IOException { 6181 byte[] block = new byte[512]; 6182 FileMetadata info = null; 6183 6184 boolean gotHeader = readTarHeader(instream, block); 6185 if (gotHeader) { 6186 try { 6187 // okay, presume we're okay, and extract the various metadata 6188 info = new FileMetadata(); 6189 info.size = extractRadix(block, 124, 12, 8); 6190 info.mtime = extractRadix(block, 136, 12, 8); 6191 info.mode = extractRadix(block, 100, 8, 8); 6192 6193 info.path = extractString(block, 345, 155); // prefix 6194 String path = extractString(block, 0, 100); 6195 if (path.length() > 0) { 6196 if (info.path.length() > 0) info.path += '/'; 6197 info.path += path; 6198 } 6199 6200 // tar link indicator field: 1 byte at offset 156 in the header. 6201 int typeChar = block[156]; 6202 if (typeChar == 'x') { 6203 // pax extended header, so we need to read that 6204 gotHeader = readPaxExtendedHeader(instream, info); 6205 if (gotHeader) { 6206 // and after a pax extended header comes another real header -- read 6207 // that to find the real file type 6208 gotHeader = readTarHeader(instream, block); 6209 } 6210 if (!gotHeader) throw new IOException("Bad or missing pax header"); 6211 6212 typeChar = block[156]; 6213 } 6214 6215 switch (typeChar) { 6216 case '0': info.type = BackupAgent.TYPE_FILE; break; 6217 case '5': { 6218 info.type = BackupAgent.TYPE_DIRECTORY; 6219 if (info.size != 0) { 6220 Slog.w(TAG, "Directory entry with nonzero size in header"); 6221 info.size = 0; 6222 } 6223 break; 6224 } 6225 case 0: { 6226 // presume EOF 6227 if (MORE_DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 6228 return null; 6229 } 6230 default: { 6231 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 6232 throw new IOException("Unknown entity type " + typeChar); 6233 } 6234 } 6235 6236 // Parse out the path 6237 // 6238 // first: apps/shared/unrecognized 6239 if (FullBackup.SHARED_PREFIX.regionMatches(0, 6240 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 6241 // File in shared storage. !!! TODO: implement this. 6242 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 6243 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 6244 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 6245 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 6246 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 6247 info.path, 0, FullBackup.APPS_PREFIX.length())) { 6248 // App content! Parse out the package name and domain 6249 6250 // strip the apps/ prefix 6251 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 6252 6253 // extract the package name 6254 int slash = info.path.indexOf('/'); 6255 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 6256 info.packageName = info.path.substring(0, slash); 6257 info.path = info.path.substring(slash+1); 6258 6259 // if it's a manifest or metadata payload we're done, otherwise parse 6260 // out the domain into which the file will be restored 6261 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 6262 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 6263 slash = info.path.indexOf('/'); 6264 if (slash < 0) { 6265 throw new IOException("Illegal semantic path in non-manifest " 6266 + info.path); 6267 } 6268 info.domain = info.path.substring(0, slash); 6269 info.path = info.path.substring(slash + 1); 6270 } 6271 } 6272 } catch (IOException e) { 6273 if (DEBUG) { 6274 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 6275 if (MORE_DEBUG) { 6276 HEXLOG(block); 6277 } 6278 } 6279 throw e; 6280 } 6281 } 6282 return info; 6283 } 6284 6285 private boolean isRestorableFile(FileMetadata info) { 6286 if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { 6287 if (MORE_DEBUG) { 6288 Slog.i(TAG, "Dropping cache file path " + info.path); 6289 } 6290 return false; 6291 } 6292 6293 if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) { 6294 // It's possible this is "no-backup" dir contents in an archive stream 6295 // produced on a device running a version of the OS that predates that 6296 // API. Respect the no-backup intention and don't let the data get to 6297 // the app. 6298 if (info.path.startsWith("no_backup/")) { 6299 if (MORE_DEBUG) { 6300 Slog.i(TAG, "Dropping no_backup file path " + info.path); 6301 } 6302 return false; 6303 } 6304 } 6305 6306 // The path needs to be canonical 6307 if (info.path.contains("..") || info.path.contains("//")) { 6308 if (MORE_DEBUG) { 6309 Slog.w(TAG, "Dropping invalid path " + info.path); 6310 } 6311 return false; 6312 } 6313 6314 // Otherwise we think this file is good to go 6315 return true; 6316 } 6317 6318 private void HEXLOG(byte[] block) { 6319 int offset = 0; 6320 int todo = block.length; 6321 StringBuilder buf = new StringBuilder(64); 6322 while (todo > 0) { 6323 buf.append(String.format("%04x ", offset)); 6324 int numThisLine = (todo > 16) ? 16 : todo; 6325 for (int i = 0; i < numThisLine; i++) { 6326 buf.append(String.format("%02x ", block[offset+i])); 6327 } 6328 Slog.i("hexdump", buf.toString()); 6329 buf.setLength(0); 6330 todo -= numThisLine; 6331 offset += numThisLine; 6332 } 6333 } 6334 6335 // Read exactly the given number of bytes into a buffer at the stated offset. 6336 // Returns false if EOF is encountered before the requested number of bytes 6337 // could be read. 6338 int readExactly(InputStream in, byte[] buffer, int offset, int size) 6339 throws IOException { 6340 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 6341 if (MORE_DEBUG) Slog.i(TAG, " ... readExactly(" + size + ") called"); 6342 int soFar = 0; 6343 while (soFar < size) { 6344 int nRead = in.read(buffer, offset + soFar, size - soFar); 6345 if (nRead <= 0) { 6346 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 6347 break; 6348 } 6349 soFar += nRead; 6350 if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar)); 6351 } 6352 return soFar; 6353 } 6354 6355 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 6356 final int got = readExactly(instream, block, 0, 512); 6357 if (got == 0) return false; // Clean EOF 6358 if (got < 512) throw new IOException("Unable to read full block header"); 6359 mBytes += 512; 6360 return true; 6361 } 6362 6363 // overwrites 'info' fields based on the pax extended header 6364 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 6365 throws IOException { 6366 // We should never see a pax extended header larger than this 6367 if (info.size > 32*1024) { 6368 Slog.w(TAG, "Suspiciously large pax header size " + info.size 6369 + " - aborting"); 6370 throw new IOException("Sanity failure: pax header size " + info.size); 6371 } 6372 6373 // read whole blocks, not just the content size 6374 int numBlocks = (int)((info.size + 511) >> 9); 6375 byte[] data = new byte[numBlocks * 512]; 6376 if (readExactly(instream, data, 0, data.length) < data.length) { 6377 throw new IOException("Unable to read full pax header"); 6378 } 6379 mBytes += data.length; 6380 6381 final int contentSize = (int) info.size; 6382 int offset = 0; 6383 do { 6384 // extract the line at 'offset' 6385 int eol = offset+1; 6386 while (eol < contentSize && data[eol] != ' ') eol++; 6387 if (eol >= contentSize) { 6388 // error: we just hit EOD looking for the end of the size field 6389 throw new IOException("Invalid pax data"); 6390 } 6391 // eol points to the space between the count and the key 6392 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 6393 int key = eol + 1; // start of key=value 6394 eol = offset + linelen - 1; // trailing LF 6395 int value; 6396 for (value = key+1; data[value] != '=' && value <= eol; value++); 6397 if (value > eol) { 6398 throw new IOException("Invalid pax declaration"); 6399 } 6400 6401 // pax requires that key/value strings be in UTF-8 6402 String keyStr = new String(data, key, value-key, "UTF-8"); 6403 // -1 to strip the trailing LF 6404 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 6405 6406 if ("path".equals(keyStr)) { 6407 info.path = valStr; 6408 } else if ("size".equals(keyStr)) { 6409 info.size = Long.parseLong(valStr); 6410 } else { 6411 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 6412 } 6413 6414 offset += linelen; 6415 } while (offset < contentSize); 6416 6417 return true; 6418 } 6419 6420 long extractRadix(byte[] data, int offset, int maxChars, int radix) 6421 throws IOException { 6422 long value = 0; 6423 final int end = offset + maxChars; 6424 for (int i = offset; i < end; i++) { 6425 final byte b = data[i]; 6426 // Numeric fields in tar can terminate with either NUL or SPC 6427 if (b == 0 || b == ' ') break; 6428 if (b < '0' || b > ('0' + radix - 1)) { 6429 throw new IOException("Invalid number in header: '" + (char)b 6430 + "' for radix " + radix); 6431 } 6432 value = radix * value + (b - '0'); 6433 } 6434 return value; 6435 } 6436 6437 String extractString(byte[] data, int offset, int maxChars) throws IOException { 6438 final int end = offset + maxChars; 6439 int eos = offset; 6440 // tar string fields terminate early with a NUL 6441 while (eos < end && data[eos] != 0) eos++; 6442 return new String(data, offset, eos-offset, "US-ASCII"); 6443 } 6444 6445 void sendStartRestore() { 6446 if (mObserver != null) { 6447 try { 6448 mObserver.onStartRestore(); 6449 } catch (RemoteException e) { 6450 Slog.w(TAG, "full restore observer went away: startRestore"); 6451 mObserver = null; 6452 } 6453 } 6454 } 6455 6456 void sendOnRestorePackage(String name) { 6457 if (mObserver != null) { 6458 try { 6459 // TODO: use a more user-friendly name string 6460 mObserver.onRestorePackage(name); 6461 } catch (RemoteException e) { 6462 Slog.w(TAG, "full restore observer went away: restorePackage"); 6463 mObserver = null; 6464 } 6465 } 6466 } 6467 6468 void sendEndRestore() { 6469 if (mObserver != null) { 6470 try { 6471 mObserver.onEndRestore(); 6472 } catch (RemoteException e) { 6473 Slog.w(TAG, "full restore observer went away: endRestore"); 6474 mObserver = null; 6475 } 6476 } 6477 } 6478 } 6479 6480 // ***** end new engine class *** 6481 6482 // Used for synchronizing doRestoreFinished during adb restore 6483 class AdbRestoreFinishedLatch implements BackupRestoreTask { 6484 static final String TAG = "AdbRestoreFinishedLatch"; 6485 final CountDownLatch mLatch; 6486 6487 AdbRestoreFinishedLatch() { 6488 mLatch = new CountDownLatch(1); 6489 } 6490 6491 void await() { 6492 boolean latched = false; 6493 try { 6494 latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); 6495 } catch (InterruptedException e) { 6496 Slog.w(TAG, "Interrupted!"); 6497 } 6498 } 6499 6500 @Override 6501 public void execute() { 6502 // Unused 6503 } 6504 6505 @Override 6506 public void operationComplete(long result) { 6507 if (MORE_DEBUG) { 6508 Slog.w(TAG, "adb onRestoreFinished() complete"); 6509 } 6510 mLatch.countDown(); 6511 } 6512 6513 @Override 6514 public void handleTimeout() { 6515 if (DEBUG) { 6516 Slog.w(TAG, "adb onRestoreFinished() timed out"); 6517 } 6518 mLatch.countDown(); 6519 } 6520 } 6521 6522 class PerformAdbRestoreTask implements Runnable { 6523 ParcelFileDescriptor mInputFile; 6524 String mCurrentPassword; 6525 String mDecryptPassword; 6526 IFullBackupRestoreObserver mObserver; 6527 AtomicBoolean mLatchObject; 6528 IBackupAgent mAgent; 6529 String mAgentPackage; 6530 ApplicationInfo mTargetApp; 6531 FullBackupObbConnection mObbConnection = null; 6532 ParcelFileDescriptor[] mPipes = null; 6533 byte[] mWidgetData = null; 6534 6535 long mBytes; 6536 6537 // Runner that can be placed on a separate thread to do in-process invocation 6538 // of the "restore finished" API asynchronously. Used by adb restore. 6539 class RestoreFinishedRunnable implements Runnable { 6540 final IBackupAgent mAgent; 6541 final int mToken; 6542 6543 RestoreFinishedRunnable(IBackupAgent agent, int token) { 6544 mAgent = agent; 6545 mToken = token; 6546 } 6547 6548 @Override 6549 public void run() { 6550 try { 6551 mAgent.doRestoreFinished(mToken, mBackupManagerBinder); 6552 } catch (RemoteException e) { 6553 // never happens; this is used only for local binder calls 6554 } 6555 } 6556 } 6557 6558 // possible handling states for a given package in the restore dataset 6559 final HashMap<String, RestorePolicy> mPackagePolicies 6560 = new HashMap<String, RestorePolicy>(); 6561 6562 // installer package names for each encountered app, derived from the manifests 6563 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>(); 6564 6565 // Signatures for a given package found in its manifest file 6566 final HashMap<String, Signature[]> mManifestSignatures 6567 = new HashMap<String, Signature[]>(); 6568 6569 // Packages we've already wiped data on when restoring their first file 6570 final HashSet<String> mClearedPackages = new HashSet<String>(); 6571 6572 PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword, 6573 IFullBackupRestoreObserver observer, AtomicBoolean latch) { 6574 mInputFile = fd; 6575 mCurrentPassword = curPassword; 6576 mDecryptPassword = decryptPassword; 6577 mObserver = observer; 6578 mLatchObject = latch; 6579 mAgent = null; 6580 mAgentPackage = null; 6581 mTargetApp = null; 6582 mObbConnection = new FullBackupObbConnection(); 6583 6584 // Which packages we've already wiped data on. We prepopulate this 6585 // with a whitelist of packages known to be unclearable. 6586 mClearedPackages.add("android"); 6587 mClearedPackages.add(SETTINGS_PACKAGE); 6588 } 6589 6590 class RestoreFileRunnable implements Runnable { 6591 IBackupAgent mAgent; 6592 FileMetadata mInfo; 6593 ParcelFileDescriptor mSocket; 6594 int mToken; 6595 6596 RestoreFileRunnable(IBackupAgent agent, FileMetadata info, 6597 ParcelFileDescriptor socket, int token) throws IOException { 6598 mAgent = agent; 6599 mInfo = info; 6600 mToken = token; 6601 6602 // This class is used strictly for process-local binder invocations. The 6603 // semantics of ParcelFileDescriptor differ in this case; in particular, we 6604 // do not automatically get a 'dup'ed descriptor that we can can continue 6605 // to use asynchronously from the caller. So, we make sure to dup it ourselves 6606 // before proceeding to do the restore. 6607 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor()); 6608 } 6609 6610 @Override 6611 public void run() { 6612 try { 6613 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type, 6614 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime, 6615 mToken, mBackupManagerBinder); 6616 } catch (RemoteException e) { 6617 // never happens; this is used strictly for local binder calls 6618 } 6619 } 6620 } 6621 6622 @Override 6623 public void run() { 6624 Slog.i(TAG, "--- Performing full-dataset restore ---"); 6625 mObbConnection.establish(); 6626 sendStartRestore(); 6627 6628 // Are we able to restore shared-storage data? 6629 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 6630 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT); 6631 } 6632 6633 FileInputStream rawInStream = null; 6634 DataInputStream rawDataIn = null; 6635 try { 6636 if (!backupPasswordMatches(mCurrentPassword)) { 6637 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting"); 6638 return; 6639 } 6640 6641 mBytes = 0; 6642 byte[] buffer = new byte[32 * 1024]; 6643 rawInStream = new FileInputStream(mInputFile.getFileDescriptor()); 6644 rawDataIn = new DataInputStream(rawInStream); 6645 6646 // First, parse out the unencrypted/uncompressed header 6647 boolean compressed = false; 6648 InputStream preCompressStream = rawInStream; 6649 final InputStream in; 6650 6651 boolean okay = false; 6652 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length(); 6653 byte[] streamHeader = new byte[headerLen]; 6654 rawDataIn.readFully(streamHeader); 6655 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8"); 6656 if (Arrays.equals(magicBytes, streamHeader)) { 6657 // okay, header looks good. now parse out the rest of the fields. 6658 String s = readHeaderLine(rawInStream); 6659 final int archiveVersion = Integer.parseInt(s); 6660 if (archiveVersion <= BACKUP_FILE_VERSION) { 6661 // okay, it's a version we recognize. if it's version 1, we may need 6662 // to try two different PBKDF2 regimes to compare checksums. 6663 final boolean pbkdf2Fallback = (archiveVersion == 1); 6664 6665 s = readHeaderLine(rawInStream); 6666 compressed = (Integer.parseInt(s) != 0); 6667 s = readHeaderLine(rawInStream); 6668 if (s.equals("none")) { 6669 // no more header to parse; we're good to go 6670 okay = true; 6671 } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) { 6672 preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback, 6673 rawInStream); 6674 if (preCompressStream != null) { 6675 okay = true; 6676 } 6677 } else Slog.w(TAG, "Archive is encrypted but no password given"); 6678 } else Slog.w(TAG, "Wrong header version: " + s); 6679 } else Slog.w(TAG, "Didn't read the right header magic"); 6680 6681 if (!okay) { 6682 Slog.w(TAG, "Invalid restore data; aborting."); 6683 return; 6684 } 6685 6686 // okay, use the right stream layer based on compression 6687 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream; 6688 6689 boolean didRestore; 6690 do { 6691 didRestore = restoreOneFile(in, buffer); 6692 } while (didRestore); 6693 6694 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes); 6695 } catch (IOException e) { 6696 Slog.e(TAG, "Unable to read restore input"); 6697 } finally { 6698 tearDownPipes(); 6699 tearDownAgent(mTargetApp, true); 6700 6701 try { 6702 if (rawDataIn != null) rawDataIn.close(); 6703 if (rawInStream != null) rawInStream.close(); 6704 mInputFile.close(); 6705 } catch (IOException e) { 6706 Slog.w(TAG, "Close of restore data pipe threw", e); 6707 /* nothing we can do about this */ 6708 } 6709 synchronized (mCurrentOpLock) { 6710 mCurrentOperations.clear(); 6711 } 6712 synchronized (mLatchObject) { 6713 mLatchObject.set(true); 6714 mLatchObject.notifyAll(); 6715 } 6716 mObbConnection.tearDown(); 6717 sendEndRestore(); 6718 Slog.d(TAG, "Full restore pass complete."); 6719 mWakelock.release(); 6720 } 6721 } 6722 6723 String readHeaderLine(InputStream in) throws IOException { 6724 int c; 6725 StringBuilder buffer = new StringBuilder(80); 6726 while ((c = in.read()) >= 0) { 6727 if (c == '\n') break; // consume and discard the newlines 6728 buffer.append((char)c); 6729 } 6730 return buffer.toString(); 6731 } 6732 6733 InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt, 6734 int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream, 6735 boolean doLog) { 6736 InputStream result = null; 6737 6738 try { 6739 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); 6740 SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt, 6741 rounds); 6742 byte[] IV = hexToByteArray(userIvHex); 6743 IvParameterSpec ivSpec = new IvParameterSpec(IV); 6744 c.init(Cipher.DECRYPT_MODE, 6745 new SecretKeySpec(userKey.getEncoded(), "AES"), 6746 ivSpec); 6747 byte[] mkCipher = hexToByteArray(masterKeyBlobHex); 6748 byte[] mkBlob = c.doFinal(mkCipher); 6749 6750 // first, the master key IV 6751 int offset = 0; 6752 int len = mkBlob[offset++]; 6753 IV = Arrays.copyOfRange(mkBlob, offset, offset + len); 6754 offset += len; 6755 // then the master key itself 6756 len = mkBlob[offset++]; 6757 byte[] mk = Arrays.copyOfRange(mkBlob, 6758 offset, offset + len); 6759 offset += len; 6760 // and finally the master key checksum hash 6761 len = mkBlob[offset++]; 6762 byte[] mkChecksum = Arrays.copyOfRange(mkBlob, 6763 offset, offset + len); 6764 6765 // now validate the decrypted master key against the checksum 6766 byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds); 6767 if (Arrays.equals(calculatedCk, mkChecksum)) { 6768 ivSpec = new IvParameterSpec(IV); 6769 c.init(Cipher.DECRYPT_MODE, 6770 new SecretKeySpec(mk, "AES"), 6771 ivSpec); 6772 // Only if all of the above worked properly will 'result' be assigned 6773 result = new CipherInputStream(rawInStream, c); 6774 } else if (doLog) Slog.w(TAG, "Incorrect password"); 6775 } catch (InvalidAlgorithmParameterException e) { 6776 if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e); 6777 } catch (BadPaddingException e) { 6778 // This case frequently occurs when the wrong password is used to decrypt 6779 // the master key. Use the identical "incorrect password" log text as is 6780 // used in the checksum failure log in order to avoid providing additional 6781 // information to an attacker. 6782 if (doLog) Slog.w(TAG, "Incorrect password"); 6783 } catch (IllegalBlockSizeException e) { 6784 if (doLog) Slog.w(TAG, "Invalid block size in master key"); 6785 } catch (NoSuchAlgorithmException e) { 6786 if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!"); 6787 } catch (NoSuchPaddingException e) { 6788 if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!"); 6789 } catch (InvalidKeyException e) { 6790 if (doLog) Slog.w(TAG, "Illegal password; aborting"); 6791 } 6792 6793 return result; 6794 } 6795 6796 InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback, 6797 InputStream rawInStream) { 6798 InputStream result = null; 6799 try { 6800 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) { 6801 6802 String userSaltHex = readHeaderLine(rawInStream); // 5 6803 byte[] userSalt = hexToByteArray(userSaltHex); 6804 6805 String ckSaltHex = readHeaderLine(rawInStream); // 6 6806 byte[] ckSalt = hexToByteArray(ckSaltHex); 6807 6808 int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7 6809 String userIvHex = readHeaderLine(rawInStream); // 8 6810 6811 String masterKeyBlobHex = readHeaderLine(rawInStream); // 9 6812 6813 // decrypt the master key blob 6814 result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt, 6815 rounds, userIvHex, masterKeyBlobHex, rawInStream, false); 6816 if (result == null && pbkdf2Fallback) { 6817 result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt, 6818 rounds, userIvHex, masterKeyBlobHex, rawInStream, true); 6819 } 6820 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName); 6821 } catch (NumberFormatException e) { 6822 Slog.w(TAG, "Can't parse restore data header"); 6823 } catch (IOException e) { 6824 Slog.w(TAG, "Can't read input header"); 6825 } 6826 6827 return result; 6828 } 6829 6830 boolean restoreOneFile(InputStream instream, byte[] buffer) { 6831 FileMetadata info; 6832 try { 6833 info = readTarHeaders(instream); 6834 if (info != null) { 6835 if (MORE_DEBUG) { 6836 dumpFileMetadata(info); 6837 } 6838 6839 final String pkg = info.packageName; 6840 if (!pkg.equals(mAgentPackage)) { 6841 // okay, change in package; set up our various 6842 // bookkeeping if we haven't seen it yet 6843 if (!mPackagePolicies.containsKey(pkg)) { 6844 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6845 } 6846 6847 // Clean up the previous agent relationship if necessary, 6848 // and let the observer know we're considering a new app. 6849 if (mAgent != null) { 6850 if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one"); 6851 // Now we're really done 6852 tearDownPipes(); 6853 tearDownAgent(mTargetApp, true); 6854 mTargetApp = null; 6855 mAgentPackage = null; 6856 } 6857 } 6858 6859 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) { 6860 mPackagePolicies.put(pkg, readAppManifest(info, instream)); 6861 mPackageInstallers.put(pkg, info.installerPackageName); 6862 // We've read only the manifest content itself at this point, 6863 // so consume the footer before looping around to the next 6864 // input file 6865 skipTarPadding(info.size, instream); 6866 sendOnRestorePackage(pkg); 6867 } else if (info.path.equals(BACKUP_METADATA_FILENAME)) { 6868 // Metadata blobs! 6869 readMetadata(info, instream); 6870 skipTarPadding(info.size, instream); 6871 } else { 6872 // Non-manifest, so it's actual file data. Is this a package 6873 // we're ignoring? 6874 boolean okay = true; 6875 RestorePolicy policy = mPackagePolicies.get(pkg); 6876 switch (policy) { 6877 case IGNORE: 6878 okay = false; 6879 break; 6880 6881 case ACCEPT_IF_APK: 6882 // If we're in accept-if-apk state, then the first file we 6883 // see MUST be the apk. 6884 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 6885 if (DEBUG) Slog.d(TAG, "APK file; installing"); 6886 // Try to install the app. 6887 String installerName = mPackageInstallers.get(pkg); 6888 okay = installApk(info, installerName, instream); 6889 // good to go; promote to ACCEPT 6890 mPackagePolicies.put(pkg, (okay) 6891 ? RestorePolicy.ACCEPT 6892 : RestorePolicy.IGNORE); 6893 // At this point we've consumed this file entry 6894 // ourselves, so just strip the tar footer and 6895 // go on to the next file in the input stream 6896 skipTarPadding(info.size, instream); 6897 return true; 6898 } else { 6899 // File data before (or without) the apk. We can't 6900 // handle it coherently in this case so ignore it. 6901 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6902 okay = false; 6903 } 6904 break; 6905 6906 case ACCEPT: 6907 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) { 6908 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT"); 6909 // we can take the data without the apk, so we 6910 // *want* to do so. skip the apk by declaring this 6911 // one file not-okay without changing the restore 6912 // policy for the package. 6913 okay = false; 6914 } 6915 break; 6916 6917 default: 6918 // Something has gone dreadfully wrong when determining 6919 // the restore policy from the manifest. Ignore the 6920 // rest of this package's data. 6921 Slog.e(TAG, "Invalid policy from manifest"); 6922 okay = false; 6923 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6924 break; 6925 } 6926 6927 // The path needs to be canonical 6928 if (info.path.contains("..") || info.path.contains("//")) { 6929 if (MORE_DEBUG) { 6930 Slog.w(TAG, "Dropping invalid path " + info.path); 6931 } 6932 okay = false; 6933 } 6934 6935 // If the policy is satisfied, go ahead and set up to pipe the 6936 // data to the agent. 6937 if (DEBUG && okay && mAgent != null) { 6938 Slog.i(TAG, "Reusing existing agent instance"); 6939 } 6940 if (okay && mAgent == null) { 6941 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg); 6942 6943 try { 6944 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0); 6945 6946 // If we haven't sent any data to this app yet, we probably 6947 // need to clear it first. Check that. 6948 if (!mClearedPackages.contains(pkg)) { 6949 // apps with their own backup agents are 6950 // responsible for coherently managing a full 6951 // restore. 6952 if (mTargetApp.backupAgentName == null) { 6953 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore"); 6954 clearApplicationDataSynchronous(pkg); 6955 } else { 6956 if (DEBUG) Slog.d(TAG, "backup agent (" 6957 + mTargetApp.backupAgentName + ") => no clear"); 6958 } 6959 mClearedPackages.add(pkg); 6960 } else { 6961 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required"); 6962 } 6963 6964 // All set; now set up the IPC and launch the agent 6965 setUpPipes(); 6966 mAgent = bindToAgentSynchronous(mTargetApp, 6967 IApplicationThread.BACKUP_MODE_RESTORE_FULL); 6968 mAgentPackage = pkg; 6969 } catch (IOException e) { 6970 // fall through to error handling 6971 } catch (NameNotFoundException e) { 6972 // fall through to error handling 6973 } 6974 6975 if (mAgent == null) { 6976 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg); 6977 okay = false; 6978 tearDownPipes(); 6979 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 6980 } 6981 } 6982 6983 // Sanity check: make sure we never give data to the wrong app. This 6984 // should never happen but a little paranoia here won't go amiss. 6985 if (okay && !pkg.equals(mAgentPackage)) { 6986 Slog.e(TAG, "Restoring data for " + pkg 6987 + " but agent is for " + mAgentPackage); 6988 okay = false; 6989 } 6990 6991 // At this point we have an agent ready to handle the full 6992 // restore data as well as a pipe for sending data to 6993 // that agent. Tell the agent to start reading from the 6994 // pipe. 6995 if (okay) { 6996 boolean agentSuccess = true; 6997 long toCopy = info.size; 6998 final int token = generateToken(); 6999 try { 7000 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null); 7001 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) { 7002 if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg 7003 + " : " + info.path); 7004 mObbConnection.restoreObbFile(pkg, mPipes[0], 7005 info.size, info.type, info.path, info.mode, 7006 info.mtime, token, mBackupManagerBinder); 7007 } else { 7008 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file " 7009 + info.path); 7010 // fire up the app's agent listening on the socket. If 7011 // the agent is running in the system process we can't 7012 // just invoke it asynchronously, so we provide a thread 7013 // for it here. 7014 if (mTargetApp.processName.equals("system")) { 7015 Slog.d(TAG, "system process agent - spinning a thread"); 7016 RestoreFileRunnable runner = new RestoreFileRunnable( 7017 mAgent, info, mPipes[0], token); 7018 new Thread(runner, "restore-sys-runner").start(); 7019 } else { 7020 mAgent.doRestoreFile(mPipes[0], info.size, info.type, 7021 info.domain, info.path, info.mode, info.mtime, 7022 token, mBackupManagerBinder); 7023 } 7024 } 7025 } catch (IOException e) { 7026 // couldn't dup the socket for a process-local restore 7027 Slog.d(TAG, "Couldn't establish restore"); 7028 agentSuccess = false; 7029 okay = false; 7030 } catch (RemoteException e) { 7031 // whoops, remote entity went away. We'll eat the content 7032 // ourselves, then, and not copy it over. 7033 Slog.e(TAG, "Agent crashed during full restore"); 7034 agentSuccess = false; 7035 okay = false; 7036 } 7037 7038 // Copy over the data if the agent is still good 7039 if (okay) { 7040 boolean pipeOkay = true; 7041 FileOutputStream pipe = new FileOutputStream( 7042 mPipes[1].getFileDescriptor()); 7043 while (toCopy > 0) { 7044 int toRead = (toCopy > buffer.length) 7045 ? buffer.length : (int)toCopy; 7046 int nRead = instream.read(buffer, 0, toRead); 7047 if (nRead >= 0) mBytes += nRead; 7048 if (nRead <= 0) break; 7049 toCopy -= nRead; 7050 7051 // send it to the output pipe as long as things 7052 // are still good 7053 if (pipeOkay) { 7054 try { 7055 pipe.write(buffer, 0, nRead); 7056 } catch (IOException e) { 7057 Slog.e(TAG, "Failed to write to restore pipe", e); 7058 pipeOkay = false; 7059 } 7060 } 7061 } 7062 7063 // done sending that file! Now we just need to consume 7064 // the delta from info.size to the end of block. 7065 skipTarPadding(info.size, instream); 7066 7067 // and now that we've sent it all, wait for the remote 7068 // side to acknowledge receipt 7069 agentSuccess = waitUntilOperationComplete(token); 7070 } 7071 7072 // okay, if the remote end failed at any point, deal with 7073 // it by ignoring the rest of the restore on it 7074 if (!agentSuccess) { 7075 if (DEBUG) { 7076 Slog.d(TAG, "Agent failure restoring " + pkg + "; now ignoring"); 7077 } 7078 mBackupHandler.removeMessages(MSG_TIMEOUT); 7079 tearDownPipes(); 7080 tearDownAgent(mTargetApp, false); 7081 mPackagePolicies.put(pkg, RestorePolicy.IGNORE); 7082 } 7083 } 7084 7085 // Problems setting up the agent communication, or an already- 7086 // ignored package: skip to the next tar stream entry by 7087 // reading and discarding this file. 7088 if (!okay) { 7089 if (DEBUG) Slog.d(TAG, "[discarding file content]"); 7090 long bytesToConsume = (info.size + 511) & ~511; 7091 while (bytesToConsume > 0) { 7092 int toRead = (bytesToConsume > buffer.length) 7093 ? buffer.length : (int)bytesToConsume; 7094 long nRead = instream.read(buffer, 0, toRead); 7095 if (nRead >= 0) mBytes += nRead; 7096 if (nRead <= 0) break; 7097 bytesToConsume -= nRead; 7098 } 7099 } 7100 } 7101 } 7102 } catch (IOException e) { 7103 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e); 7104 // treat as EOF 7105 info = null; 7106 } 7107 7108 return (info != null); 7109 } 7110 7111 void setUpPipes() throws IOException { 7112 mPipes = ParcelFileDescriptor.createPipe(); 7113 } 7114 7115 void tearDownPipes() { 7116 if (mPipes != null) { 7117 try { 7118 mPipes[0].close(); 7119 mPipes[0] = null; 7120 mPipes[1].close(); 7121 mPipes[1] = null; 7122 } catch (IOException e) { 7123 Slog.w(TAG, "Couldn't close agent pipes", e); 7124 } 7125 mPipes = null; 7126 } 7127 } 7128 7129 void tearDownAgent(ApplicationInfo app, boolean doRestoreFinished) { 7130 if (mAgent != null) { 7131 try { 7132 // In the adb restore case, we do restore-finished here 7133 if (doRestoreFinished) { 7134 final int token = generateToken(); 7135 final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(); 7136 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, latch); 7137 if (mTargetApp.processName.equals("system")) { 7138 if (MORE_DEBUG) { 7139 Slog.d(TAG, "system agent - restoreFinished on thread"); 7140 } 7141 Runnable runner = new RestoreFinishedRunnable(mAgent, token); 7142 new Thread(runner, "restore-sys-finished-runner").start(); 7143 } else { 7144 mAgent.doRestoreFinished(token, mBackupManagerBinder); 7145 } 7146 7147 latch.await(); 7148 } 7149 7150 // unbind and tidy up even on timeout or failure, just in case 7151 mActivityManager.unbindBackupAgent(app); 7152 7153 // The agent was running with a stub Application object, so shut it down. 7154 // !!! We hardcode the confirmation UI's package name here rather than use a 7155 // manifest flag! TODO something less direct. 7156 if (app.uid >= Process.FIRST_APPLICATION_UID 7157 && !app.packageName.equals("com.android.backupconfirm")) { 7158 if (DEBUG) Slog.d(TAG, "Killing host process"); 7159 mActivityManager.killApplicationProcess(app.processName, app.uid); 7160 } else { 7161 if (DEBUG) Slog.d(TAG, "Not killing after full restore"); 7162 } 7163 } catch (RemoteException e) { 7164 Slog.d(TAG, "Lost app trying to shut down"); 7165 } 7166 mAgent = null; 7167 } 7168 } 7169 7170 class RestoreInstallObserver extends PackageInstallObserver { 7171 final AtomicBoolean mDone = new AtomicBoolean(); 7172 String mPackageName; 7173 int mResult; 7174 7175 public void reset() { 7176 synchronized (mDone) { 7177 mDone.set(false); 7178 } 7179 } 7180 7181 public void waitForCompletion() { 7182 synchronized (mDone) { 7183 while (mDone.get() == false) { 7184 try { 7185 mDone.wait(); 7186 } catch (InterruptedException e) { } 7187 } 7188 } 7189 } 7190 7191 int getResult() { 7192 return mResult; 7193 } 7194 7195 @Override 7196 public void onPackageInstalled(String packageName, int returnCode, 7197 String msg, Bundle extras) { 7198 synchronized (mDone) { 7199 mResult = returnCode; 7200 mPackageName = packageName; 7201 mDone.set(true); 7202 mDone.notifyAll(); 7203 } 7204 } 7205 } 7206 7207 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub { 7208 final AtomicBoolean mDone = new AtomicBoolean(); 7209 int mResult; 7210 7211 public void reset() { 7212 synchronized (mDone) { 7213 mDone.set(false); 7214 } 7215 } 7216 7217 public void waitForCompletion() { 7218 synchronized (mDone) { 7219 while (mDone.get() == false) { 7220 try { 7221 mDone.wait(); 7222 } catch (InterruptedException e) { } 7223 } 7224 } 7225 } 7226 7227 @Override 7228 public void packageDeleted(String packageName, int returnCode) throws RemoteException { 7229 synchronized (mDone) { 7230 mResult = returnCode; 7231 mDone.set(true); 7232 mDone.notifyAll(); 7233 } 7234 } 7235 } 7236 7237 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver(); 7238 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver(); 7239 7240 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) { 7241 boolean okay = true; 7242 7243 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName); 7244 7245 // The file content is an .apk file. Copy it out to a staging location and 7246 // attempt to install it. 7247 File apkFile = new File(mDataDir, info.packageName); 7248 try { 7249 FileOutputStream apkStream = new FileOutputStream(apkFile); 7250 byte[] buffer = new byte[32 * 1024]; 7251 long size = info.size; 7252 while (size > 0) { 7253 long toRead = (buffer.length < size) ? buffer.length : size; 7254 int didRead = instream.read(buffer, 0, (int)toRead); 7255 if (didRead >= 0) mBytes += didRead; 7256 apkStream.write(buffer, 0, didRead); 7257 size -= didRead; 7258 } 7259 apkStream.close(); 7260 7261 // make sure the installer can read it 7262 apkFile.setReadable(true, false); 7263 7264 // Now install it 7265 Uri packageUri = Uri.fromFile(apkFile); 7266 mInstallObserver.reset(); 7267 mPackageManager.installPackage(packageUri, mInstallObserver, 7268 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB, 7269 installerPackage); 7270 mInstallObserver.waitForCompletion(); 7271 7272 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) { 7273 // The only time we continue to accept install of data even if the 7274 // apk install failed is if we had already determined that we could 7275 // accept the data regardless. 7276 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) { 7277 okay = false; 7278 } 7279 } else { 7280 // Okay, the install succeeded. Make sure it was the right app. 7281 boolean uninstall = false; 7282 if (!mInstallObserver.mPackageName.equals(info.packageName)) { 7283 Slog.w(TAG, "Restore stream claimed to include apk for " 7284 + info.packageName + " but apk was really " 7285 + mInstallObserver.mPackageName); 7286 // delete the package we just put in place; it might be fraudulent 7287 okay = false; 7288 uninstall = true; 7289 } else { 7290 try { 7291 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName, 7292 PackageManager.GET_SIGNATURES); 7293 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { 7294 Slog.w(TAG, "Restore stream contains apk of package " 7295 + info.packageName + " but it disallows backup/restore"); 7296 okay = false; 7297 } else { 7298 // So far so good -- do the signatures match the manifest? 7299 Signature[] sigs = mManifestSignatures.get(info.packageName); 7300 if (signaturesMatch(sigs, pkg)) { 7301 // If this is a system-uid app without a declared backup agent, 7302 // don't restore any of the file data. 7303 if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID) 7304 && (pkg.applicationInfo.backupAgentName == null)) { 7305 Slog.w(TAG, "Installed app " + info.packageName 7306 + " has restricted uid and no agent"); 7307 okay = false; 7308 } 7309 } else { 7310 Slog.w(TAG, "Installed app " + info.packageName 7311 + " signatures do not match restore manifest"); 7312 okay = false; 7313 uninstall = true; 7314 } 7315 } 7316 } catch (NameNotFoundException e) { 7317 Slog.w(TAG, "Install of package " + info.packageName 7318 + " succeeded but now not found"); 7319 okay = false; 7320 } 7321 } 7322 7323 // If we're not okay at this point, we need to delete the package 7324 // that we just installed. 7325 if (uninstall) { 7326 mDeleteObserver.reset(); 7327 mPackageManager.deletePackage(mInstallObserver.mPackageName, 7328 mDeleteObserver, 0); 7329 mDeleteObserver.waitForCompletion(); 7330 } 7331 } 7332 } catch (IOException e) { 7333 Slog.e(TAG, "Unable to transcribe restored apk for install"); 7334 okay = false; 7335 } finally { 7336 apkFile.delete(); 7337 } 7338 7339 return okay; 7340 } 7341 7342 // Given an actual file content size, consume the post-content padding mandated 7343 // by the tar format. 7344 void skipTarPadding(long size, InputStream instream) throws IOException { 7345 long partial = (size + 512) % 512; 7346 if (partial > 0) { 7347 final int needed = 512 - (int)partial; 7348 byte[] buffer = new byte[needed]; 7349 if (readExactly(instream, buffer, 0, needed) == needed) { 7350 mBytes += needed; 7351 } else throw new IOException("Unexpected EOF in padding"); 7352 } 7353 } 7354 7355 // Read a widget metadata file, returning the restored blob 7356 void readMetadata(FileMetadata info, InputStream instream) throws IOException { 7357 // Fail on suspiciously large widget dump files 7358 if (info.size > 64 * 1024) { 7359 throw new IOException("Metadata too big; corrupt? size=" + info.size); 7360 } 7361 7362 byte[] buffer = new byte[(int) info.size]; 7363 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 7364 mBytes += info.size; 7365 } else throw new IOException("Unexpected EOF in widget data"); 7366 7367 String[] str = new String[1]; 7368 int offset = extractLine(buffer, 0, str); 7369 int version = Integer.parseInt(str[0]); 7370 if (version == BACKUP_MANIFEST_VERSION) { 7371 offset = extractLine(buffer, offset, str); 7372 final String pkg = str[0]; 7373 if (info.packageName.equals(pkg)) { 7374 // Data checks out -- the rest of the buffer is a concatenation of 7375 // binary blobs as described in the comment at writeAppWidgetData() 7376 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 7377 offset, buffer.length - offset); 7378 DataInputStream in = new DataInputStream(bin); 7379 while (bin.available() > 0) { 7380 int token = in.readInt(); 7381 int size = in.readInt(); 7382 if (size > 64 * 1024) { 7383 throw new IOException("Datum " 7384 + Integer.toHexString(token) 7385 + " too big; corrupt? size=" + info.size); 7386 } 7387 switch (token) { 7388 case BACKUP_WIDGET_METADATA_TOKEN: 7389 { 7390 if (MORE_DEBUG) { 7391 Slog.i(TAG, "Got widget metadata for " + info.packageName); 7392 } 7393 mWidgetData = new byte[size]; 7394 in.read(mWidgetData); 7395 break; 7396 } 7397 default: 7398 { 7399 if (DEBUG) { 7400 Slog.i(TAG, "Ignoring metadata blob " 7401 + Integer.toHexString(token) 7402 + " for " + info.packageName); 7403 } 7404 in.skipBytes(size); 7405 break; 7406 } 7407 } 7408 } 7409 } else { 7410 Slog.w(TAG, "Metadata mismatch: package " + info.packageName 7411 + " but widget data for " + pkg); 7412 } 7413 } else { 7414 Slog.w(TAG, "Unsupported metadata version " + version); 7415 } 7416 } 7417 7418 // Returns a policy constant; takes a buffer arg to reduce memory churn 7419 RestorePolicy readAppManifest(FileMetadata info, InputStream instream) 7420 throws IOException { 7421 // Fail on suspiciously large manifest files 7422 if (info.size > 64 * 1024) { 7423 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 7424 } 7425 7426 byte[] buffer = new byte[(int) info.size]; 7427 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) { 7428 mBytes += info.size; 7429 } else throw new IOException("Unexpected EOF in manifest"); 7430 7431 RestorePolicy policy = RestorePolicy.IGNORE; 7432 String[] str = new String[1]; 7433 int offset = 0; 7434 7435 try { 7436 offset = extractLine(buffer, offset, str); 7437 int version = Integer.parseInt(str[0]); 7438 if (version == BACKUP_MANIFEST_VERSION) { 7439 offset = extractLine(buffer, offset, str); 7440 String manifestPackage = str[0]; 7441 // TODO: handle <original-package> 7442 if (manifestPackage.equals(info.packageName)) { 7443 offset = extractLine(buffer, offset, str); 7444 version = Integer.parseInt(str[0]); // app version 7445 offset = extractLine(buffer, offset, str); 7446 // This is the platform version, which we don't use, but we parse it 7447 // as a safety against corruption in the manifest. 7448 Integer.parseInt(str[0]); 7449 offset = extractLine(buffer, offset, str); 7450 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 7451 offset = extractLine(buffer, offset, str); 7452 boolean hasApk = str[0].equals("1"); 7453 offset = extractLine(buffer, offset, str); 7454 int numSigs = Integer.parseInt(str[0]); 7455 if (numSigs > 0) { 7456 Signature[] sigs = new Signature[numSigs]; 7457 for (int i = 0; i < numSigs; i++) { 7458 offset = extractLine(buffer, offset, str); 7459 sigs[i] = new Signature(str[0]); 7460 } 7461 mManifestSignatures.put(info.packageName, sigs); 7462 7463 // Okay, got the manifest info we need... 7464 try { 7465 PackageInfo pkgInfo = mPackageManager.getPackageInfo( 7466 info.packageName, PackageManager.GET_SIGNATURES); 7467 // Fall through to IGNORE if the app explicitly disallows backup 7468 final int flags = pkgInfo.applicationInfo.flags; 7469 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 7470 // Restore system-uid-space packages only if they have 7471 // defined a custom backup agent 7472 if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 7473 || (pkgInfo.applicationInfo.backupAgentName != null)) { 7474 // Verify signatures against any installed version; if they 7475 // don't match, then we fall though and ignore the data. The 7476 // signatureMatch() method explicitly ignores the signature 7477 // check for packages installed on the system partition, because 7478 // such packages are signed with the platform cert instead of 7479 // the app developer's cert, so they're different on every 7480 // device. 7481 if (signaturesMatch(sigs, pkgInfo)) { 7482 if (pkgInfo.versionCode >= version) { 7483 Slog.i(TAG, "Sig + version match; taking data"); 7484 policy = RestorePolicy.ACCEPT; 7485 } else { 7486 // The data is from a newer version of the app than 7487 // is presently installed. That means we can only 7488 // use it if the matching apk is also supplied. 7489 Slog.d(TAG, "Data version " + version 7490 + " is newer than installed version " 7491 + pkgInfo.versionCode + " - requiring apk"); 7492 policy = RestorePolicy.ACCEPT_IF_APK; 7493 } 7494 } else { 7495 Slog.w(TAG, "Restore manifest signatures do not match " 7496 + "installed application for " + info.packageName); 7497 } 7498 } else { 7499 Slog.w(TAG, "Package " + info.packageName 7500 + " is system level with no agent"); 7501 } 7502 } else { 7503 if (DEBUG) Slog.i(TAG, "Restore manifest from " 7504 + info.packageName + " but allowBackup=false"); 7505 } 7506 } catch (NameNotFoundException e) { 7507 // Okay, the target app isn't installed. We can process 7508 // the restore properly only if the dataset provides the 7509 // apk file and we can successfully install it. 7510 if (DEBUG) Slog.i(TAG, "Package " + info.packageName 7511 + " not installed; requiring apk in dataset"); 7512 policy = RestorePolicy.ACCEPT_IF_APK; 7513 } 7514 7515 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) { 7516 Slog.i(TAG, "Cannot restore package " + info.packageName 7517 + " without the matching .apk"); 7518 } 7519 } else { 7520 Slog.i(TAG, "Missing signature on backed-up package " 7521 + info.packageName); 7522 } 7523 } else { 7524 Slog.i(TAG, "Expected package " + info.packageName 7525 + " but restore manifest claims " + manifestPackage); 7526 } 7527 } else { 7528 Slog.i(TAG, "Unknown restore manifest version " + version 7529 + " for package " + info.packageName); 7530 } 7531 } catch (NumberFormatException e) { 7532 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 7533 } catch (IllegalArgumentException e) { 7534 Slog.w(TAG, e.getMessage()); 7535 } 7536 7537 return policy; 7538 } 7539 7540 // Builds a line from a byte buffer starting at 'offset', and returns 7541 // the index of the next unconsumed data in the buffer. 7542 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 7543 final int end = buffer.length; 7544 if (offset >= end) throw new IOException("Incomplete data"); 7545 7546 int pos; 7547 for (pos = offset; pos < end; pos++) { 7548 byte c = buffer[pos]; 7549 // at LF we declare end of line, and return the next char as the 7550 // starting point for the next time through 7551 if (c == '\n') { 7552 break; 7553 } 7554 } 7555 outStr[0] = new String(buffer, offset, pos - offset); 7556 pos++; // may be pointing an extra byte past the end but that's okay 7557 return pos; 7558 } 7559 7560 void dumpFileMetadata(FileMetadata info) { 7561 if (DEBUG) { 7562 StringBuilder b = new StringBuilder(128); 7563 7564 // mode string 7565 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-'); 7566 b.append(((info.mode & 0400) != 0) ? 'r' : '-'); 7567 b.append(((info.mode & 0200) != 0) ? 'w' : '-'); 7568 b.append(((info.mode & 0100) != 0) ? 'x' : '-'); 7569 b.append(((info.mode & 0040) != 0) ? 'r' : '-'); 7570 b.append(((info.mode & 0020) != 0) ? 'w' : '-'); 7571 b.append(((info.mode & 0010) != 0) ? 'x' : '-'); 7572 b.append(((info.mode & 0004) != 0) ? 'r' : '-'); 7573 b.append(((info.mode & 0002) != 0) ? 'w' : '-'); 7574 b.append(((info.mode & 0001) != 0) ? 'x' : '-'); 7575 b.append(String.format(" %9d ", info.size)); 7576 7577 Date stamp = new Date(info.mtime); 7578 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp)); 7579 7580 b.append(info.packageName); 7581 b.append(" :: "); 7582 b.append(info.domain); 7583 b.append(" :: "); 7584 b.append(info.path); 7585 7586 Slog.i(TAG, b.toString()); 7587 } 7588 } 7589 // Consume a tar file header block [sequence] and accumulate the relevant metadata 7590 FileMetadata readTarHeaders(InputStream instream) throws IOException { 7591 byte[] block = new byte[512]; 7592 FileMetadata info = null; 7593 7594 boolean gotHeader = readTarHeader(instream, block); 7595 if (gotHeader) { 7596 try { 7597 // okay, presume we're okay, and extract the various metadata 7598 info = new FileMetadata(); 7599 info.size = extractRadix(block, 124, 12, 8); 7600 info.mtime = extractRadix(block, 136, 12, 8); 7601 info.mode = extractRadix(block, 100, 8, 8); 7602 7603 info.path = extractString(block, 345, 155); // prefix 7604 String path = extractString(block, 0, 100); 7605 if (path.length() > 0) { 7606 if (info.path.length() > 0) info.path += '/'; 7607 info.path += path; 7608 } 7609 7610 // tar link indicator field: 1 byte at offset 156 in the header. 7611 int typeChar = block[156]; 7612 if (typeChar == 'x') { 7613 // pax extended header, so we need to read that 7614 gotHeader = readPaxExtendedHeader(instream, info); 7615 if (gotHeader) { 7616 // and after a pax extended header comes another real header -- read 7617 // that to find the real file type 7618 gotHeader = readTarHeader(instream, block); 7619 } 7620 if (!gotHeader) throw new IOException("Bad or missing pax header"); 7621 7622 typeChar = block[156]; 7623 } 7624 7625 switch (typeChar) { 7626 case '0': info.type = BackupAgent.TYPE_FILE; break; 7627 case '5': { 7628 info.type = BackupAgent.TYPE_DIRECTORY; 7629 if (info.size != 0) { 7630 Slog.w(TAG, "Directory entry with nonzero size in header"); 7631 info.size = 0; 7632 } 7633 break; 7634 } 7635 case 0: { 7636 // presume EOF 7637 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 7638 return null; 7639 } 7640 default: { 7641 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 7642 throw new IOException("Unknown entity type " + typeChar); 7643 } 7644 } 7645 7646 // Parse out the path 7647 // 7648 // first: apps/shared/unrecognized 7649 if (FullBackup.SHARED_PREFIX.regionMatches(0, 7650 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 7651 // File in shared storage. !!! TODO: implement this. 7652 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 7653 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 7654 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 7655 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); 7656 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 7657 info.path, 0, FullBackup.APPS_PREFIX.length())) { 7658 // App content! Parse out the package name and domain 7659 7660 // strip the apps/ prefix 7661 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 7662 7663 // extract the package name 7664 int slash = info.path.indexOf('/'); 7665 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path); 7666 info.packageName = info.path.substring(0, slash); 7667 info.path = info.path.substring(slash+1); 7668 7669 // if it's a manifest or metadata payload we're done, otherwise parse 7670 // out the domain into which the file will be restored 7671 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) 7672 && !info.path.equals(BACKUP_METADATA_FILENAME)) { 7673 slash = info.path.indexOf('/'); 7674 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path); 7675 info.domain = info.path.substring(0, slash); 7676 info.path = info.path.substring(slash + 1); 7677 } 7678 } 7679 } catch (IOException e) { 7680 if (DEBUG) { 7681 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 7682 HEXLOG(block); 7683 } 7684 throw e; 7685 } 7686 } 7687 return info; 7688 } 7689 7690 private void HEXLOG(byte[] block) { 7691 int offset = 0; 7692 int todo = block.length; 7693 StringBuilder buf = new StringBuilder(64); 7694 while (todo > 0) { 7695 buf.append(String.format("%04x ", offset)); 7696 int numThisLine = (todo > 16) ? 16 : todo; 7697 for (int i = 0; i < numThisLine; i++) { 7698 buf.append(String.format("%02x ", block[offset+i])); 7699 } 7700 Slog.i("hexdump", buf.toString()); 7701 buf.setLength(0); 7702 todo -= numThisLine; 7703 offset += numThisLine; 7704 } 7705 } 7706 7707 // Read exactly the given number of bytes into a buffer at the stated offset. 7708 // Returns false if EOF is encountered before the requested number of bytes 7709 // could be read. 7710 int readExactly(InputStream in, byte[] buffer, int offset, int size) 7711 throws IOException { 7712 if (size <= 0) throw new IllegalArgumentException("size must be > 0"); 7713 7714 int soFar = 0; 7715 while (soFar < size) { 7716 int nRead = in.read(buffer, offset + soFar, size - soFar); 7717 if (nRead <= 0) { 7718 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 7719 break; 7720 } 7721 soFar += nRead; 7722 } 7723 return soFar; 7724 } 7725 7726 boolean readTarHeader(InputStream instream, byte[] block) throws IOException { 7727 final int got = readExactly(instream, block, 0, 512); 7728 if (got == 0) return false; // Clean EOF 7729 if (got < 512) throw new IOException("Unable to read full block header"); 7730 mBytes += 512; 7731 return true; 7732 } 7733 7734 // overwrites 'info' fields based on the pax extended header 7735 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info) 7736 throws IOException { 7737 // We should never see a pax extended header larger than this 7738 if (info.size > 32*1024) { 7739 Slog.w(TAG, "Suspiciously large pax header size " + info.size 7740 + " - aborting"); 7741 throw new IOException("Sanity failure: pax header size " + info.size); 7742 } 7743 7744 // read whole blocks, not just the content size 7745 int numBlocks = (int)((info.size + 511) >> 9); 7746 byte[] data = new byte[numBlocks * 512]; 7747 if (readExactly(instream, data, 0, data.length) < data.length) { 7748 throw new IOException("Unable to read full pax header"); 7749 } 7750 mBytes += data.length; 7751 7752 final int contentSize = (int) info.size; 7753 int offset = 0; 7754 do { 7755 // extract the line at 'offset' 7756 int eol = offset+1; 7757 while (eol < contentSize && data[eol] != ' ') eol++; 7758 if (eol >= contentSize) { 7759 // error: we just hit EOD looking for the end of the size field 7760 throw new IOException("Invalid pax data"); 7761 } 7762 // eol points to the space between the count and the key 7763 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 7764 int key = eol + 1; // start of key=value 7765 eol = offset + linelen - 1; // trailing LF 7766 int value; 7767 for (value = key+1; data[value] != '=' && value <= eol; value++); 7768 if (value > eol) { 7769 throw new IOException("Invalid pax declaration"); 7770 } 7771 7772 // pax requires that key/value strings be in UTF-8 7773 String keyStr = new String(data, key, value-key, "UTF-8"); 7774 // -1 to strip the trailing LF 7775 String valStr = new String(data, value+1, eol-value-1, "UTF-8"); 7776 7777 if ("path".equals(keyStr)) { 7778 info.path = valStr; 7779 } else if ("size".equals(keyStr)) { 7780 info.size = Long.parseLong(valStr); 7781 } else { 7782 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key); 7783 } 7784 7785 offset += linelen; 7786 } while (offset < contentSize); 7787 7788 return true; 7789 } 7790 7791 long extractRadix(byte[] data, int offset, int maxChars, int radix) 7792 throws IOException { 7793 long value = 0; 7794 final int end = offset + maxChars; 7795 for (int i = offset; i < end; i++) { 7796 final byte b = data[i]; 7797 // Numeric fields in tar can terminate with either NUL or SPC 7798 if (b == 0 || b == ' ') break; 7799 if (b < '0' || b > ('0' + radix - 1)) { 7800 throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix); 7801 } 7802 value = radix * value + (b - '0'); 7803 } 7804 return value; 7805 } 7806 7807 String extractString(byte[] data, int offset, int maxChars) throws IOException { 7808 final int end = offset + maxChars; 7809 int eos = offset; 7810 // tar string fields terminate early with a NUL 7811 while (eos < end && data[eos] != 0) eos++; 7812 return new String(data, offset, eos-offset, "US-ASCII"); 7813 } 7814 7815 void sendStartRestore() { 7816 if (mObserver != null) { 7817 try { 7818 mObserver.onStartRestore(); 7819 } catch (RemoteException e) { 7820 Slog.w(TAG, "full restore observer went away: startRestore"); 7821 mObserver = null; 7822 } 7823 } 7824 } 7825 7826 void sendOnRestorePackage(String name) { 7827 if (mObserver != null) { 7828 try { 7829 // TODO: use a more user-friendly name string 7830 mObserver.onRestorePackage(name); 7831 } catch (RemoteException e) { 7832 Slog.w(TAG, "full restore observer went away: restorePackage"); 7833 mObserver = null; 7834 } 7835 } 7836 } 7837 7838 void sendEndRestore() { 7839 if (mObserver != null) { 7840 try { 7841 mObserver.onEndRestore(); 7842 } catch (RemoteException e) { 7843 Slog.w(TAG, "full restore observer went away: endRestore"); 7844 mObserver = null; 7845 } 7846 } 7847 } 7848 } 7849 7850 // ----- Restore handling ----- 7851 7852 // Old style: directly match the stored vs on device signature blocks 7853 static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { 7854 if (target == null) { 7855 return false; 7856 } 7857 7858 // If the target resides on the system partition, we allow it to restore 7859 // data from the like-named package in a restore set even if the signatures 7860 // do not match. (Unlike general applications, those flashed to the system 7861 // partition will be signed with the device's platform certificate, so on 7862 // different phones the same system app will have different signatures.) 7863 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 7864 if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 7865 return true; 7866 } 7867 7868 // Allow unsigned apps, but not signed on one device and unsigned on the other 7869 // !!! TODO: is this the right policy? 7870 Signature[] deviceSigs = target.signatures; 7871 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs 7872 + " device=" + deviceSigs); 7873 if ((storedSigs == null || storedSigs.length == 0) 7874 && (deviceSigs == null || deviceSigs.length == 0)) { 7875 return true; 7876 } 7877 if (storedSigs == null || deviceSigs == null) { 7878 return false; 7879 } 7880 7881 // !!! TODO: this demands that every stored signature match one 7882 // that is present on device, and does not demand the converse. 7883 // Is this this right policy? 7884 int nStored = storedSigs.length; 7885 int nDevice = deviceSigs.length; 7886 7887 for (int i=0; i < nStored; i++) { 7888 boolean match = false; 7889 for (int j=0; j < nDevice; j++) { 7890 if (storedSigs[i].equals(deviceSigs[j])) { 7891 match = true; 7892 break; 7893 } 7894 } 7895 if (!match) { 7896 return false; 7897 } 7898 } 7899 return true; 7900 } 7901 7902 // Used by both incremental and full restore 7903 void restoreWidgetData(String packageName, byte[] widgetData) { 7904 // Apply the restored widget state and generate the ID update for the app 7905 // TODO: http://b/22388012 7906 if (MORE_DEBUG) { 7907 Slog.i(TAG, "Incorporating restored widget data"); 7908 } 7909 AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM); 7910 } 7911 7912 // ***************************** 7913 // NEW UNIFIED RESTORE IMPLEMENTATION 7914 // ***************************** 7915 7916 // states of the unified-restore state machine 7917 enum UnifiedRestoreState { 7918 INITIAL, 7919 RUNNING_QUEUE, 7920 RESTORE_KEYVALUE, 7921 RESTORE_FULL, 7922 RESTORE_FINISHED, 7923 FINAL 7924 } 7925 7926 class PerformUnifiedRestoreTask implements BackupRestoreTask { 7927 // Transport we're working with to do the restore 7928 private IBackupTransport mTransport; 7929 7930 // Where per-transport saved state goes 7931 File mStateDir; 7932 7933 // Restore observer; may be null 7934 private IRestoreObserver mObserver; 7935 7936 // Token identifying the dataset to the transport 7937 private long mToken; 7938 7939 // When this is a restore-during-install, this is the token identifying the 7940 // operation to the Package Manager, and we must ensure that we let it know 7941 // when we're finished. 7942 private int mPmToken; 7943 7944 // When this is restore-during-install, we need to tell the package manager 7945 // whether we actually launched the app, because this affects notifications 7946 // around externally-visible state transitions. 7947 private boolean mDidLaunch; 7948 7949 // Is this a whole-system restore, i.e. are we establishing a new ancestral 7950 // dataset to base future restore-at-install operations from? 7951 private boolean mIsSystemRestore; 7952 7953 // If this is a single-package restore, what package are we interested in? 7954 private PackageInfo mTargetPackage; 7955 7956 // In all cases, the calculated list of packages that we are trying to restore 7957 private List<PackageInfo> mAcceptSet; 7958 7959 // Our bookkeeping about the ancestral dataset 7960 private PackageManagerBackupAgent mPmAgent; 7961 7962 // Currently-bound backup agent for restore + restoreFinished purposes 7963 private IBackupAgent mAgent; 7964 7965 // What sort of restore we're doing now 7966 private RestoreDescription mRestoreDescription; 7967 7968 // The package we're currently restoring 7969 private PackageInfo mCurrentPackage; 7970 7971 // Widget-related data handled as part of this restore operation 7972 private byte[] mWidgetData; 7973 7974 // Number of apps restored in this pass 7975 private int mCount; 7976 7977 // When did we start? 7978 private long mStartRealtime; 7979 7980 // State machine progress 7981 private UnifiedRestoreState mState; 7982 7983 // How are things going? 7984 private int mStatus; 7985 7986 // Done? 7987 private boolean mFinished; 7988 7989 // Key/value: bookkeeping about staged data and files for agent access 7990 private File mBackupDataName; 7991 private File mStageName; 7992 private File mSavedStateName; 7993 private File mNewStateName; 7994 ParcelFileDescriptor mBackupData; 7995 ParcelFileDescriptor mNewState; 7996 7997 // Invariant: mWakelock is already held, and this task is responsible for 7998 // releasing it at the end of the restore operation. 7999 PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer, 8000 long restoreSetToken, PackageInfo targetPackage, int pmToken, 8001 boolean isFullSystemRestore, String[] filterSet) { 8002 mState = UnifiedRestoreState.INITIAL; 8003 mStartRealtime = SystemClock.elapsedRealtime(); 8004 8005 mTransport = transport; 8006 mObserver = observer; 8007 mToken = restoreSetToken; 8008 mPmToken = pmToken; 8009 mTargetPackage = targetPackage; 8010 mIsSystemRestore = isFullSystemRestore; 8011 mFinished = false; 8012 mDidLaunch = false; 8013 8014 if (targetPackage != null) { 8015 // Single package restore 8016 mAcceptSet = new ArrayList<PackageInfo>(); 8017 mAcceptSet.add(targetPackage); 8018 } else { 8019 // Everything possible, or a target set 8020 if (filterSet == null) { 8021 // We want everything and a pony 8022 List<PackageInfo> apps = 8023 PackageManagerBackupAgent.getStorableApplications(mPackageManager); 8024 filterSet = packagesToNames(apps); 8025 if (DEBUG) { 8026 Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps"); 8027 } 8028 } 8029 8030 mAcceptSet = new ArrayList<PackageInfo>(filterSet.length); 8031 8032 // Pro tem, we insist on moving the settings provider package to last place. 8033 // Keep track of whether it's in the list, and bump it down if so. We also 8034 // want to do the system package itself first if it's called for. 8035 boolean hasSystem = false; 8036 boolean hasSettings = false; 8037 for (int i = 0; i < filterSet.length; i++) { 8038 try { 8039 PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0); 8040 if ("android".equals(info.packageName)) { 8041 hasSystem = true; 8042 continue; 8043 } 8044 if (SETTINGS_PACKAGE.equals(info.packageName)) { 8045 hasSettings = true; 8046 continue; 8047 } 8048 8049 if (appIsEligibleForBackup(info.applicationInfo)) { 8050 mAcceptSet.add(info); 8051 } 8052 } catch (NameNotFoundException e) { 8053 // requested package name doesn't exist; ignore it 8054 } 8055 } 8056 if (hasSystem) { 8057 try { 8058 mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0)); 8059 } catch (NameNotFoundException e) { 8060 // won't happen; we know a priori that it's valid 8061 } 8062 } 8063 if (hasSettings) { 8064 try { 8065 mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0)); 8066 } catch (NameNotFoundException e) { 8067 // this one is always valid too 8068 } 8069 } 8070 } 8071 8072 if (MORE_DEBUG) { 8073 Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size()); 8074 for (PackageInfo info : mAcceptSet) { 8075 Slog.v(TAG, " " + info.packageName); 8076 } 8077 } 8078 } 8079 8080 private String[] packagesToNames(List<PackageInfo> apps) { 8081 final int N = apps.size(); 8082 String[] names = new String[N]; 8083 for (int i = 0; i < N; i++) { 8084 names[i] = apps.get(i).packageName; 8085 } 8086 return names; 8087 } 8088 8089 // Execute one tick of whatever state machine the task implements 8090 @Override 8091 public void execute() { 8092 if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState); 8093 switch (mState) { 8094 case INITIAL: 8095 startRestore(); 8096 break; 8097 8098 case RUNNING_QUEUE: 8099 dispatchNextRestore(); 8100 break; 8101 8102 case RESTORE_KEYVALUE: 8103 restoreKeyValue(); 8104 break; 8105 8106 case RESTORE_FULL: 8107 restoreFull(); 8108 break; 8109 8110 case RESTORE_FINISHED: 8111 restoreFinished(); 8112 break; 8113 8114 case FINAL: 8115 if (!mFinished) finalizeRestore(); 8116 else { 8117 Slog.e(TAG, "Duplicate finish"); 8118 } 8119 mFinished = true; 8120 break; 8121 } 8122 } 8123 8124 /* 8125 * SKETCH OF OPERATION 8126 * 8127 * create one of these PerformUnifiedRestoreTask objects, telling it which 8128 * dataset & transport to address, and then parameters within the restore 8129 * operation: single target package vs many, etc. 8130 * 8131 * 1. transport.startRestore(token, list-of-packages). If we need @pm@ it is 8132 * always placed first and the settings provider always placed last [for now]. 8133 * 8134 * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline] 8135 * 8136 * [ state change => RUNNING_QUEUE ] 8137 * 8138 * NOW ITERATE: 8139 * 8140 * { 3. t.nextRestorePackage() 8141 * 4. does the metadata for this package allow us to restore it? 8142 * does the on-disk app permit us to restore it? [re-check allowBackup etc] 8143 * 5. is this a key/value dataset? => key/value agent restore 8144 * [ state change => RESTORE_KEYVALUE ] 8145 * 5a. spin up agent 8146 * 5b. t.getRestoreData() to stage it properly 8147 * 5c. call into agent to perform restore 8148 * 5d. tear down agent 8149 * [ state change => RUNNING_QUEUE ] 8150 * 8151 * 6. else it's a stream dataset: 8152 * [ state change => RESTORE_FULL ] 8153 * 6a. instantiate the engine for a stream restore: engine handles agent lifecycles 8154 * 6b. spin off engine runner on separate thread 8155 * 6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket 8156 * [ state change => RUNNING_QUEUE ] 8157 * } 8158 * 8159 * [ state change => FINAL ] 8160 * 8161 * 7. t.finishRestore(), release wakelock, etc. 8162 * 8163 * 8164 */ 8165 8166 // state INITIAL : set up for the restore and read the metadata if necessary 8167 private void startRestore() { 8168 sendStartRestore(mAcceptSet.size()); 8169 8170 // If we're starting a full-system restore, set up to begin widget ID remapping 8171 if (mIsSystemRestore) { 8172 // TODO: http://b/22388012 8173 AppWidgetBackupBridge.restoreStarting(UserHandle.USER_SYSTEM); 8174 } 8175 8176 try { 8177 String transportDir = mTransport.transportDirName(); 8178 mStateDir = new File(mBaseStateDir, transportDir); 8179 8180 // Fetch the current metadata from the dataset first 8181 PackageInfo pmPackage = new PackageInfo(); 8182 pmPackage.packageName = PACKAGE_MANAGER_SENTINEL; 8183 mAcceptSet.add(0, pmPackage); 8184 8185 PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]); 8186 mStatus = mTransport.startRestore(mToken, packages); 8187 if (mStatus != BackupTransport.TRANSPORT_OK) { 8188 Slog.e(TAG, "Transport error " + mStatus + "; no restore possible"); 8189 mStatus = BackupTransport.TRANSPORT_ERROR; 8190 executeNextState(UnifiedRestoreState.FINAL); 8191 return; 8192 } 8193 8194 RestoreDescription desc = mTransport.nextRestorePackage(); 8195 if (desc == null) { 8196 Slog.e(TAG, "No restore metadata available; halting"); 8197 mStatus = BackupTransport.TRANSPORT_ERROR; 8198 executeNextState(UnifiedRestoreState.FINAL); 8199 return; 8200 } 8201 if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) { 8202 Slog.e(TAG, "Required metadata but got " + desc.getPackageName()); 8203 mStatus = BackupTransport.TRANSPORT_ERROR; 8204 executeNextState(UnifiedRestoreState.FINAL); 8205 return; 8206 } 8207 8208 // Pull the Package Manager metadata from the restore set first 8209 mCurrentPackage = new PackageInfo(); 8210 mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL; 8211 mPmAgent = new PackageManagerBackupAgent(mPackageManager, null); 8212 mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind()); 8213 if (MORE_DEBUG) { 8214 Slog.v(TAG, "initiating restore for PMBA"); 8215 } 8216 initiateOneRestore(mCurrentPackage, 0); 8217 // The PM agent called operationComplete() already, because our invocation 8218 // of it is process-local and therefore synchronous. That means that the 8219 // next-state message (RUNNING_QUEUE) is already enqueued. Only if we're 8220 // unable to proceed with running the queue do we remove that pending 8221 // message and jump straight to the FINAL state. Because this was 8222 // synchronous we also know that we should cancel the pending timeout 8223 // message. 8224 mBackupHandler.removeMessages(MSG_TIMEOUT); 8225 8226 // Verify that the backup set includes metadata. If not, we can't do 8227 // signature/version verification etc, so we simply do not proceed with 8228 // the restore operation. 8229 if (!mPmAgent.hasMetadata()) { 8230 Slog.e(TAG, "No restore metadata available, so not restoring"); 8231 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8232 PACKAGE_MANAGER_SENTINEL, 8233 "Package manager restore metadata missing"); 8234 mStatus = BackupTransport.TRANSPORT_ERROR; 8235 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 8236 executeNextState(UnifiedRestoreState.FINAL); 8237 return; 8238 } 8239 8240 // Success; cache the metadata and continue as expected with the 8241 // next state already enqueued 8242 8243 } catch (Exception e) { 8244 // If we lost the transport at any time, halt 8245 Slog.e(TAG, "Unable to contact transport for restore: " + e.getMessage()); 8246 mStatus = BackupTransport.TRANSPORT_ERROR; 8247 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); 8248 executeNextState(UnifiedRestoreState.FINAL); 8249 return; 8250 } 8251 } 8252 8253 // state RUNNING_QUEUE : figure out what the next thing to be restored is, 8254 // and fire the appropriate next step 8255 private void dispatchNextRestore() { 8256 UnifiedRestoreState nextState = UnifiedRestoreState.FINAL; 8257 try { 8258 mRestoreDescription = mTransport.nextRestorePackage(); 8259 final String pkgName = (mRestoreDescription != null) 8260 ? mRestoreDescription.getPackageName() : null; 8261 if (pkgName == null) { 8262 Slog.e(TAG, "Failure getting next package name"); 8263 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8264 nextState = UnifiedRestoreState.FINAL; 8265 return; 8266 } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) { 8267 // Yay we've reached the end cleanly 8268 if (DEBUG) { 8269 Slog.v(TAG, "No more packages; finishing restore"); 8270 } 8271 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); 8272 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis); 8273 nextState = UnifiedRestoreState.FINAL; 8274 return; 8275 } 8276 8277 if (DEBUG) { 8278 Slog.i(TAG, "Next restore package: " + mRestoreDescription); 8279 } 8280 sendOnRestorePackage(pkgName); 8281 8282 Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName); 8283 if (metaInfo == null) { 8284 Slog.e(TAG, "No metadata for " + pkgName); 8285 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 8286 "Package metadata missing"); 8287 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8288 return; 8289 } 8290 8291 try { 8292 mCurrentPackage = mPackageManager.getPackageInfo( 8293 pkgName, PackageManager.GET_SIGNATURES); 8294 } catch (NameNotFoundException e) { 8295 // Whoops, we thought we could restore this package but it 8296 // turns out not to be present. Skip it. 8297 Slog.e(TAG, "Package not present: " + pkgName); 8298 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, 8299 "Package missing on device"); 8300 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8301 return; 8302 } 8303 8304 if (metaInfo.versionCode > mCurrentPackage.versionCode) { 8305 // Data is from a "newer" version of the app than we have currently 8306 // installed. If the app has not declared that it is prepared to 8307 // handle this case, we do not attempt the restore. 8308 if ((mCurrentPackage.applicationInfo.flags 8309 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) { 8310 String message = "Version " + metaInfo.versionCode 8311 + " > installed version " + mCurrentPackage.versionCode; 8312 Slog.w(TAG, "Package " + pkgName + ": " + message); 8313 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8314 pkgName, message); 8315 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8316 return; 8317 } else { 8318 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode 8319 + " > installed " + mCurrentPackage.versionCode 8320 + " but restoreAnyVersion"); 8321 } 8322 } 8323 8324 if (MORE_DEBUG) Slog.v(TAG, "Package " + pkgName 8325 + " restore version [" + metaInfo.versionCode 8326 + "] is compatible with installed version [" 8327 + mCurrentPackage.versionCode + "]"); 8328 8329 // Reset per-package preconditions and fire the appropriate next state 8330 mWidgetData = null; 8331 final int type = mRestoreDescription.getDataType(); 8332 if (type == RestoreDescription.TYPE_KEY_VALUE) { 8333 nextState = UnifiedRestoreState.RESTORE_KEYVALUE; 8334 } else if (type == RestoreDescription.TYPE_FULL_STREAM) { 8335 nextState = UnifiedRestoreState.RESTORE_FULL; 8336 } else { 8337 // Unknown restore type; ignore this package and move on 8338 Slog.e(TAG, "Unrecognized restore type " + type); 8339 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8340 return; 8341 } 8342 } catch (Exception e) { 8343 Slog.e(TAG, "Can't get next restore target from transport; halting: " 8344 + e.getMessage()); 8345 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8346 nextState = UnifiedRestoreState.FINAL; 8347 return; 8348 } finally { 8349 executeNextState(nextState); 8350 } 8351 } 8352 8353 // state RESTORE_KEYVALUE : restore one package via key/value API set 8354 private void restoreKeyValue() { 8355 // Initiating the restore will pass responsibility for the state machine's 8356 // progress to the agent callback, so we do not always execute the 8357 // next state here. 8358 final String packageName = mCurrentPackage.packageName; 8359 // Validate some semantic requirements that apply in this way 8360 // only to the key/value restore API flow 8361 if (mCurrentPackage.applicationInfo.backupAgentName == null 8362 || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) { 8363 if (MORE_DEBUG) { 8364 Slog.i(TAG, "Data exists for package " + packageName 8365 + " but app has no agent; skipping"); 8366 } 8367 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8368 "Package has no agent"); 8369 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8370 return; 8371 } 8372 8373 Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); 8374 if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { 8375 Slog.w(TAG, "Signature mismatch restoring " + packageName); 8376 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8377 "Signature mismatch"); 8378 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8379 return; 8380 } 8381 8382 // Good to go! Set up and bind the agent... 8383 mAgent = bindToAgentSynchronous( 8384 mCurrentPackage.applicationInfo, 8385 IApplicationThread.BACKUP_MODE_INCREMENTAL); 8386 if (mAgent == null) { 8387 Slog.w(TAG, "Can't find backup agent for " + packageName); 8388 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, 8389 "Restore agent missing"); 8390 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8391 return; 8392 } 8393 8394 // Whatever happens next, we've launched the target app now; remember that. 8395 mDidLaunch = true; 8396 8397 // And then finally start the restore on this agent 8398 try { 8399 initiateOneRestore(mCurrentPackage, metaInfo.versionCode); 8400 ++mCount; 8401 } catch (Exception e) { 8402 Slog.e(TAG, "Error when attempting restore: " + e.toString()); 8403 keyValueAgentErrorCleanup(); 8404 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8405 } 8406 } 8407 8408 // Guts of a key/value restore operation 8409 void initiateOneRestore(PackageInfo app, int appVersionCode) { 8410 final String packageName = app.packageName; 8411 8412 if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName); 8413 8414 // !!! TODO: get the dirs from the transport 8415 mBackupDataName = new File(mDataDir, packageName + ".restore"); 8416 mStageName = new File(mDataDir, packageName + ".stage"); 8417 mNewStateName = new File(mStateDir, packageName + ".new"); 8418 mSavedStateName = new File(mStateDir, packageName); 8419 8420 // don't stage the 'android' package where the wallpaper data lives. this is 8421 // an optimization: we know there's no widget data hosted/published by that 8422 // package, and this way we avoid doing a spurious copy of MB-sized wallpaper 8423 // data following the download. 8424 boolean staging = !packageName.equals("android"); 8425 ParcelFileDescriptor stage; 8426 File downloadFile = (staging) ? mStageName : mBackupDataName; 8427 8428 final int token = generateToken(); 8429 try { 8430 // Run the transport's restore pass 8431 stage = ParcelFileDescriptor.open(downloadFile, 8432 ParcelFileDescriptor.MODE_READ_WRITE | 8433 ParcelFileDescriptor.MODE_CREATE | 8434 ParcelFileDescriptor.MODE_TRUNCATE); 8435 8436 if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) { 8437 // Transport-level failure, so we wind everything up and 8438 // terminate the restore operation. 8439 Slog.e(TAG, "Error getting restore data for " + packageName); 8440 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8441 stage.close(); 8442 downloadFile.delete(); 8443 executeNextState(UnifiedRestoreState.FINAL); 8444 return; 8445 } 8446 8447 // We have the data from the transport. Now we extract and strip 8448 // any per-package metadata (typically widget-related information) 8449 // if appropriate 8450 if (staging) { 8451 stage.close(); 8452 stage = ParcelFileDescriptor.open(downloadFile, 8453 ParcelFileDescriptor.MODE_READ_ONLY); 8454 8455 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 8456 ParcelFileDescriptor.MODE_READ_WRITE | 8457 ParcelFileDescriptor.MODE_CREATE | 8458 ParcelFileDescriptor.MODE_TRUNCATE); 8459 8460 BackupDataInput in = new BackupDataInput(stage.getFileDescriptor()); 8461 BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor()); 8462 byte[] buffer = new byte[8192]; // will grow when needed 8463 while (in.readNextHeader()) { 8464 final String key = in.getKey(); 8465 final int size = in.getDataSize(); 8466 8467 // is this a special key? 8468 if (key.equals(KEY_WIDGET_STATE)) { 8469 if (DEBUG) { 8470 Slog.i(TAG, "Restoring widget state for " + packageName); 8471 } 8472 mWidgetData = new byte[size]; 8473 in.readEntityData(mWidgetData, 0, size); 8474 } else { 8475 if (size > buffer.length) { 8476 buffer = new byte[size]; 8477 } 8478 in.readEntityData(buffer, 0, size); 8479 out.writeEntityHeader(key, size); 8480 out.writeEntityData(buffer, size); 8481 } 8482 } 8483 8484 mBackupData.close(); 8485 } 8486 8487 // Okay, we have the data. Now have the agent do the restore. 8488 stage.close(); 8489 8490 mBackupData = ParcelFileDescriptor.open(mBackupDataName, 8491 ParcelFileDescriptor.MODE_READ_ONLY); 8492 8493 mNewState = ParcelFileDescriptor.open(mNewStateName, 8494 ParcelFileDescriptor.MODE_READ_WRITE | 8495 ParcelFileDescriptor.MODE_CREATE | 8496 ParcelFileDescriptor.MODE_TRUNCATE); 8497 8498 // Kick off the restore, checking for hung agents. The timeout or 8499 // the operationComplete() callback will schedule the next step, 8500 // so we do not do that here. 8501 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this); 8502 mAgent.doRestore(mBackupData, appVersionCode, mNewState, 8503 token, mBackupManagerBinder); 8504 } catch (Exception e) { 8505 Slog.e(TAG, "Unable to call app for restore: " + packageName, e); 8506 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8507 packageName, e.toString()); 8508 keyValueAgentErrorCleanup(); // clears any pending timeout messages as well 8509 8510 // After a restore failure we go back to running the queue. If there 8511 // are no more packages to be restored that will be handled by the 8512 // next step. 8513 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8514 } 8515 } 8516 8517 // state RESTORE_FULL : restore one package via streaming engine 8518 private void restoreFull() { 8519 // None of this can run on the work looper here, so we spin asynchronous 8520 // work like this: 8521 // 8522 // StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk() 8523 // write it into the pipe to the engine 8524 // EngineThread: FullRestoreEngine thread communicating with the target app 8525 // 8526 // When finished, StreamFeederThread executes next state as appropriate on the 8527 // backup looper, and the overall unified restore task resumes 8528 try { 8529 StreamFeederThread feeder = new StreamFeederThread(); 8530 if (MORE_DEBUG) { 8531 Slog.i(TAG, "Spinning threads for stream restore of " 8532 + mCurrentPackage.packageName); 8533 } 8534 new Thread(feeder, "unified-stream-feeder").start(); 8535 8536 // At this point the feeder is responsible for advancing the restore 8537 // state, so we're done here. 8538 } catch (IOException e) { 8539 // Unable to instantiate the feeder thread -- we need to bail on the 8540 // current target. We haven't asked the transport for data yet, though, 8541 // so we can do that simply by going back to running the restore queue. 8542 Slog.e(TAG, "Unable to construct pipes for stream restore!"); 8543 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8544 } 8545 } 8546 8547 // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end 8548 private void restoreFinished() { 8549 try { 8550 final int token = generateToken(); 8551 prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this); 8552 mAgent.doRestoreFinished(token, mBackupManagerBinder); 8553 // If we get this far, the callback or timeout will schedule the 8554 // next restore state, so we're done 8555 } catch (Exception e) { 8556 final String packageName = mCurrentPackage.packageName; 8557 Slog.e(TAG, "Unable to finalize restore of " + packageName); 8558 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8559 packageName, e.toString()); 8560 keyValueAgentErrorCleanup(); 8561 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8562 } 8563 } 8564 8565 class StreamFeederThread extends RestoreEngine implements Runnable, BackupRestoreTask { 8566 final String TAG = "StreamFeederThread"; 8567 FullRestoreEngine mEngine; 8568 EngineThread mEngineThread; 8569 8570 // pipe through which we read data from the transport. [0] read, [1] write 8571 ParcelFileDescriptor[] mTransportPipes; 8572 8573 // pipe through which the engine will read data. [0] read, [1] write 8574 ParcelFileDescriptor[] mEnginePipes; 8575 8576 public StreamFeederThread() throws IOException { 8577 mTransportPipes = ParcelFileDescriptor.createPipe(); 8578 mEnginePipes = ParcelFileDescriptor.createPipe(); 8579 setRunning(true); 8580 } 8581 8582 @Override 8583 public void run() { 8584 UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE; 8585 int status = BackupTransport.TRANSPORT_OK; 8586 8587 EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE, 8588 mCurrentPackage.packageName); 8589 8590 mEngine = new FullRestoreEngine(this, null, mCurrentPackage, false, false); 8591 mEngineThread = new EngineThread(mEngine, mEnginePipes[0]); 8592 8593 ParcelFileDescriptor eWriteEnd = mEnginePipes[1]; 8594 ParcelFileDescriptor tReadEnd = mTransportPipes[0]; 8595 ParcelFileDescriptor tWriteEnd = mTransportPipes[1]; 8596 8597 int bufferSize = 32 * 1024; 8598 byte[] buffer = new byte[bufferSize]; 8599 FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor()); 8600 FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor()); 8601 8602 // spin up the engine and start moving data to it 8603 new Thread(mEngineThread, "unified-restore-engine").start(); 8604 8605 try { 8606 while (status == BackupTransport.TRANSPORT_OK) { 8607 // have the transport write some of the restoring data to us 8608 int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd); 8609 if (result > 0) { 8610 // The transport wrote this many bytes of restore data to the 8611 // pipe, so pass it along to the engine. 8612 if (MORE_DEBUG) { 8613 Slog.v(TAG, " <- transport provided chunk size " + result); 8614 } 8615 if (result > bufferSize) { 8616 bufferSize = result; 8617 buffer = new byte[bufferSize]; 8618 } 8619 int toCopy = result; 8620 while (toCopy > 0) { 8621 int n = transportIn.read(buffer, 0, toCopy); 8622 engineOut.write(buffer, 0, n); 8623 toCopy -= n; 8624 if (MORE_DEBUG) { 8625 Slog.v(TAG, " -> wrote " + n + " to engine, left=" + toCopy); 8626 } 8627 } 8628 } else if (result == BackupTransport.NO_MORE_DATA) { 8629 // Clean finish. Wind up and we're done! 8630 if (MORE_DEBUG) { 8631 Slog.i(TAG, "Got clean full-restore EOF for " 8632 + mCurrentPackage.packageName); 8633 } 8634 status = BackupTransport.TRANSPORT_OK; 8635 break; 8636 } else { 8637 // Transport reported some sort of failure; the fall-through 8638 // handling will deal properly with that. 8639 Slog.e(TAG, "Error " + result + " streaming restore for " 8640 + mCurrentPackage.packageName); 8641 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8642 status = result; 8643 } 8644 } 8645 if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through"); 8646 } catch (IOException e) { 8647 // We lost our ability to communicate via the pipes. That's worrying 8648 // but potentially recoverable; abandon this package's restore but 8649 // carry on with the next restore target. 8650 Slog.e(TAG, "Unable to route data for restore"); 8651 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8652 mCurrentPackage.packageName, "I/O error on pipes"); 8653 status = BackupTransport.AGENT_ERROR; 8654 } catch (Exception e) { 8655 // The transport threw; terminate the whole operation. Closing 8656 // the sockets will wake up the engine and it will then tidy up the 8657 // remote end. 8658 Slog.e(TAG, "Transport failed during restore: " + e.getMessage()); 8659 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); 8660 status = BackupTransport.TRANSPORT_ERROR; 8661 } finally { 8662 // Close the transport pipes and *our* end of the engine pipe, 8663 // but leave the engine thread's end open so that it properly 8664 // hits EOF and winds up its operations. 8665 IoUtils.closeQuietly(mEnginePipes[1]); 8666 IoUtils.closeQuietly(mTransportPipes[0]); 8667 IoUtils.closeQuietly(mTransportPipes[1]); 8668 8669 // Don't proceed until the engine has wound up operations 8670 mEngineThread.waitForResult(); 8671 8672 // Now we're really done with this one too 8673 IoUtils.closeQuietly(mEnginePipes[0]); 8674 8675 // In all cases we want to remember whether we launched 8676 // the target app as part of our work so far. 8677 mDidLaunch = (mEngine.getAgent() != null); 8678 8679 // If we hit a transport-level error, we are done with everything; 8680 // if we hit an agent error we just go back to running the queue. 8681 if (status == BackupTransport.TRANSPORT_OK) { 8682 // Clean finish means we issue the restore-finished callback 8683 nextState = UnifiedRestoreState.RESTORE_FINISHED; 8684 8685 // the engine bound the target's agent, so recover that binding 8686 // to use for the callback. 8687 mAgent = mEngine.getAgent(); 8688 8689 // and the restored widget data, if any 8690 mWidgetData = mEngine.getWidgetData(); 8691 } else { 8692 // Something went wrong somewhere. Whether it was at the transport 8693 // level is immaterial; we need to tell the transport to bail 8694 try { 8695 mTransport.abortFullRestore(); 8696 } catch (Exception e) { 8697 // transport itself is dead; make sure we handle this as a 8698 // fatal error 8699 Slog.e(TAG, "Transport threw from abortFullRestore: " + e.getMessage()); 8700 status = BackupTransport.TRANSPORT_ERROR; 8701 } 8702 8703 // We also need to wipe the current target's data, as it's probably 8704 // in an incoherent state. 8705 clearApplicationDataSynchronous(mCurrentPackage.packageName); 8706 8707 // Schedule the next state based on the nature of our failure 8708 if (status == BackupTransport.TRANSPORT_ERROR) { 8709 nextState = UnifiedRestoreState.FINAL; 8710 } else { 8711 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8712 } 8713 } 8714 executeNextState(nextState); 8715 setRunning(false); 8716 } 8717 } 8718 8719 // BackupRestoreTask interface, specifically for timeout handling 8720 8721 @Override 8722 public void execute() { /* intentionally empty */ } 8723 8724 @Override 8725 public void operationComplete(long result) { /* intentionally empty */ } 8726 8727 // The app has timed out handling a restoring file 8728 @Override 8729 public void handleTimeout() { 8730 if (DEBUG) { 8731 Slog.w(TAG, "Full-data restore target timed out; shutting down"); 8732 } 8733 mEngineThread.handleTimeout(); 8734 8735 IoUtils.closeQuietly(mEnginePipes[1]); 8736 mEnginePipes[1] = null; 8737 IoUtils.closeQuietly(mEnginePipes[0]); 8738 mEnginePipes[0] = null; 8739 } 8740 } 8741 8742 class EngineThread implements Runnable { 8743 FullRestoreEngine mEngine; 8744 FileInputStream mEngineStream; 8745 8746 EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) { 8747 mEngine = engine; 8748 engine.setRunning(true); 8749 // We *do* want this FileInputStream to own the underlying fd, so that 8750 // when we are finished with it, it closes this end of the pipe in a way 8751 // that signals its other end. 8752 mEngineStream = new FileInputStream(engineSocket.getFileDescriptor(), true); 8753 } 8754 8755 public boolean isRunning() { 8756 return mEngine.isRunning(); 8757 } 8758 8759 public int waitForResult() { 8760 return mEngine.waitForResult(); 8761 } 8762 8763 @Override 8764 public void run() { 8765 try { 8766 while (mEngine.isRunning()) { 8767 // Tell it to be sure to leave the agent instance up after finishing 8768 mEngine.restoreOneFile(mEngineStream, false); 8769 } 8770 } finally { 8771 // Because mEngineStream adopted its underlying FD, this also 8772 // closes this end of the pipe. 8773 IoUtils.closeQuietly(mEngineStream); 8774 } 8775 } 8776 8777 public void handleTimeout() { 8778 IoUtils.closeQuietly(mEngineStream); 8779 mEngine.handleTimeout(); 8780 } 8781 } 8782 8783 // state FINAL : tear everything down and we're done. 8784 private void finalizeRestore() { 8785 if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver); 8786 8787 try { 8788 mTransport.finishRestore(); 8789 } catch (Exception e) { 8790 Slog.e(TAG, "Error finishing restore", e); 8791 } 8792 8793 // Tell the observer we're done 8794 if (mObserver != null) { 8795 try { 8796 mObserver.restoreFinished(mStatus); 8797 } catch (RemoteException e) { 8798 Slog.d(TAG, "Restore observer died at restoreFinished"); 8799 } 8800 } 8801 8802 // Clear any ongoing session timeout. 8803 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 8804 8805 // If we have a PM token, we must under all circumstances be sure to 8806 // handshake when we've finished. 8807 if (mPmToken > 0) { 8808 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken); 8809 try { 8810 mPackageManagerBinder.finishPackageInstall(mPmToken, mDidLaunch); 8811 } catch (RemoteException e) { /* can't happen */ } 8812 } else { 8813 // We were invoked via an active restore session, not by the Package 8814 // Manager, so start up the session timeout again. 8815 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, 8816 TIMEOUT_RESTORE_INTERVAL); 8817 } 8818 8819 // Kick off any work that may be needed regarding app widget restores 8820 // TODO: http://b/22388012 8821 AppWidgetBackupBridge.restoreFinished(UserHandle.USER_SYSTEM); 8822 8823 // If this was a full-system restore, record the ancestral 8824 // dataset information 8825 if (mIsSystemRestore && mPmAgent != null) { 8826 mAncestralPackages = mPmAgent.getRestoredPackages(); 8827 mAncestralToken = mToken; 8828 writeRestoreTokens(); 8829 } 8830 8831 // done; we can finally release the wakelock and be legitimately done. 8832 Slog.i(TAG, "Restore complete."); 8833 mWakelock.release(); 8834 } 8835 8836 void keyValueAgentErrorCleanup() { 8837 // If the agent fails restore, it might have put the app's data 8838 // into an incoherent state. For consistency we wipe its data 8839 // again in this case before continuing with normal teardown 8840 clearApplicationDataSynchronous(mCurrentPackage.packageName); 8841 keyValueAgentCleanup(); 8842 } 8843 8844 // TODO: clean up naming; this is now used at finish by both k/v and stream restores 8845 void keyValueAgentCleanup() { 8846 mBackupDataName.delete(); 8847 mStageName.delete(); 8848 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {} 8849 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {} 8850 mBackupData = mNewState = null; 8851 8852 // if everything went okay, remember the recorded state now 8853 // 8854 // !!! TODO: the restored data could be migrated on the server 8855 // side into the current dataset. In that case the new state file 8856 // we just created would reflect the data already extant in the 8857 // backend, so there'd be nothing more to do. Until that happens, 8858 // however, we need to make sure that we record the data to the 8859 // current backend dataset. (Yes, this means shipping the data over 8860 // the wire in both directions. That's bad, but consistency comes 8861 // first, then efficiency.) Once we introduce server-side data 8862 // migration to the newly-restored device's dataset, we will change 8863 // the following from a discard of the newly-written state to the 8864 // "correct" operation of renaming into the canonical state blob. 8865 mNewStateName.delete(); // TODO: remove; see above comment 8866 //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this 8867 8868 // If this wasn't the PM pseudopackage, tear down the agent side 8869 if (mCurrentPackage.applicationInfo != null) { 8870 // unbind and tidy up even on timeout or failure 8871 try { 8872 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo); 8873 8874 // The agent was probably running with a stub Application object, 8875 // which isn't a valid run mode for the main app logic. Shut 8876 // down the app so that next time it's launched, it gets the 8877 // usual full initialization. Note that this is only done for 8878 // full-system restores: when a single app has requested a restore, 8879 // it is explicitly not killed following that operation. 8880 // 8881 // We execute this kill when these conditions hold: 8882 // 1. it's not a system-uid process, 8883 // 2. the app did not request its own restore (mTargetPackage == null), and either 8884 // 3a. the app is a full-data target (TYPE_FULL_STREAM) or 8885 // b. the app does not state android:killAfterRestore="false" in its manifest 8886 final int appFlags = mCurrentPackage.applicationInfo.flags; 8887 final boolean killAfterRestore = 8888 (mCurrentPackage.applicationInfo.uid >= Process.FIRST_APPLICATION_UID) 8889 && ((mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM) 8890 || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0)); 8891 8892 if (mTargetPackage == null && killAfterRestore) { 8893 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of " 8894 + mCurrentPackage.applicationInfo.processName); 8895 mActivityManager.killApplicationProcess( 8896 mCurrentPackage.applicationInfo.processName, 8897 mCurrentPackage.applicationInfo.uid); 8898 } 8899 } catch (RemoteException e) { 8900 // can't happen; we run in the same process as the activity manager 8901 } 8902 } 8903 8904 // The caller is responsible for reestablishing the state machine; our 8905 // responsibility here is to clear the decks for whatever comes next. 8906 mBackupHandler.removeMessages(MSG_TIMEOUT, this); 8907 synchronized (mCurrentOpLock) { 8908 mCurrentOperations.clear(); 8909 } 8910 } 8911 8912 @Override 8913 public void operationComplete(long unusedResult) { 8914 if (MORE_DEBUG) { 8915 Slog.i(TAG, "operationComplete() during restore: target=" 8916 + mCurrentPackage.packageName 8917 + " state=" + mState); 8918 } 8919 8920 final UnifiedRestoreState nextState; 8921 switch (mState) { 8922 case INITIAL: 8923 // We've just (manually) restored the PMBA. It doesn't need the 8924 // additional restore-finished callback so we bypass that and go 8925 // directly to running the queue. 8926 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8927 break; 8928 8929 case RESTORE_KEYVALUE: 8930 case RESTORE_FULL: { 8931 // Okay, we've just heard back from the agent that it's done with 8932 // the restore itself. We now have to send the same agent its 8933 // doRestoreFinished() callback, so roll into that state. 8934 nextState = UnifiedRestoreState.RESTORE_FINISHED; 8935 break; 8936 } 8937 8938 case RESTORE_FINISHED: { 8939 // Okay, we're done with this package. Tidy up and go on to the next 8940 // app in the queue. 8941 int size = (int) mBackupDataName.length(); 8942 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, 8943 mCurrentPackage.packageName, size); 8944 8945 // Just go back to running the restore queue 8946 keyValueAgentCleanup(); 8947 8948 // If there was widget state associated with this app, get the OS to 8949 // incorporate it into current bookeeping and then pass that along to 8950 // the app as part of the restore-time work. 8951 if (mWidgetData != null) { 8952 restoreWidgetData(mCurrentPackage.packageName, mWidgetData); 8953 } 8954 8955 nextState = UnifiedRestoreState.RUNNING_QUEUE; 8956 break; 8957 } 8958 8959 default: { 8960 // Some kind of horrible semantic error; we're in an unexpected state. 8961 // Back off hard and wind up. 8962 Slog.e(TAG, "Unexpected restore callback into state " + mState); 8963 keyValueAgentErrorCleanup(); 8964 nextState = UnifiedRestoreState.FINAL; 8965 break; 8966 } 8967 } 8968 8969 executeNextState(nextState); 8970 } 8971 8972 // A call to agent.doRestore() or agent.doRestoreFinished() has timed out 8973 @Override 8974 public void handleTimeout() { 8975 Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName); 8976 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, 8977 mCurrentPackage.packageName, "restore timeout"); 8978 // Handle like an agent that threw on invocation: wipe it and go on to the next 8979 keyValueAgentErrorCleanup(); 8980 executeNextState(UnifiedRestoreState.RUNNING_QUEUE); 8981 } 8982 8983 void executeNextState(UnifiedRestoreState nextState) { 8984 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on " 8985 + this + " nextState=" + nextState); 8986 mState = nextState; 8987 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this); 8988 mBackupHandler.sendMessage(msg); 8989 } 8990 8991 // restore observer support 8992 void sendStartRestore(int numPackages) { 8993 if (mObserver != null) { 8994 try { 8995 mObserver.restoreStarting(numPackages); 8996 } catch (RemoteException e) { 8997 Slog.w(TAG, "Restore observer went away: startRestore"); 8998 mObserver = null; 8999 } 9000 } 9001 } 9002 9003 void sendOnRestorePackage(String name) { 9004 if (mObserver != null) { 9005 if (mObserver != null) { 9006 try { 9007 mObserver.onUpdate(mCount, name); 9008 } catch (RemoteException e) { 9009 Slog.d(TAG, "Restore observer died in onUpdate"); 9010 mObserver = null; 9011 } 9012 } 9013 } 9014 } 9015 9016 void sendEndRestore() { 9017 if (mObserver != null) { 9018 try { 9019 mObserver.restoreFinished(mStatus); 9020 } catch (RemoteException e) { 9021 Slog.w(TAG, "Restore observer went away: endRestore"); 9022 mObserver = null; 9023 } 9024 } 9025 } 9026 } 9027 9028 class PerformClearTask implements Runnable { 9029 IBackupTransport mTransport; 9030 PackageInfo mPackage; 9031 9032 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) { 9033 mTransport = transport; 9034 mPackage = packageInfo; 9035 } 9036 9037 public void run() { 9038 try { 9039 // Clear the on-device backup state to ensure a full backup next time 9040 File stateDir = new File(mBaseStateDir, mTransport.transportDirName()); 9041 File stateFile = new File(stateDir, mPackage.packageName); 9042 stateFile.delete(); 9043 9044 // Tell the transport to remove all the persistent storage for the app 9045 // TODO - need to handle failures 9046 mTransport.clearBackupData(mPackage); 9047 } catch (Exception e) { 9048 Slog.e(TAG, "Transport threw clearing data for " + mPackage + ": " + e.getMessage()); 9049 } finally { 9050 try { 9051 // TODO - need to handle failures 9052 mTransport.finishBackup(); 9053 } catch (Exception e) { 9054 // Nothing we can do here, alas 9055 Slog.e(TAG, "Unable to mark clear operation finished: " + e.getMessage()); 9056 } 9057 9058 // Last but not least, release the cpu 9059 mWakelock.release(); 9060 } 9061 } 9062 } 9063 9064 class PerformInitializeTask implements Runnable { 9065 HashSet<String> mQueue; 9066 9067 PerformInitializeTask(HashSet<String> transportNames) { 9068 mQueue = transportNames; 9069 } 9070 9071 public void run() { 9072 try { 9073 for (String transportName : mQueue) { 9074 IBackupTransport transport = getTransport(transportName); 9075 if (transport == null) { 9076 Slog.e(TAG, "Requested init for " + transportName + " but not found"); 9077 continue; 9078 } 9079 9080 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName); 9081 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName()); 9082 long startRealtime = SystemClock.elapsedRealtime(); 9083 int status = transport.initializeDevice(); 9084 9085 if (status == BackupTransport.TRANSPORT_OK) { 9086 status = transport.finishBackup(); 9087 } 9088 9089 // Okay, the wipe really happened. Clean up our local bookkeeping. 9090 if (status == BackupTransport.TRANSPORT_OK) { 9091 Slog.i(TAG, "Device init successful"); 9092 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); 9093 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); 9094 resetBackupState(new File(mBaseStateDir, transport.transportDirName())); 9095 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis); 9096 synchronized (mQueueLock) { 9097 recordInitPendingLocked(false, transportName); 9098 } 9099 } else { 9100 // If this didn't work, requeue this one and try again 9101 // after a suitable interval 9102 Slog.e(TAG, "Transport error in initializeDevice()"); 9103 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); 9104 synchronized (mQueueLock) { 9105 recordInitPendingLocked(true, transportName); 9106 } 9107 // do this via another alarm to make sure of the wakelock states 9108 long delay = transport.requestBackupTime(); 9109 Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay); 9110 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 9111 System.currentTimeMillis() + delay, mRunInitIntent); 9112 } 9113 } 9114 } catch (Exception e) { 9115 Slog.e(TAG, "Unexpected error performing init", e); 9116 } finally { 9117 // Done; release the wakelock 9118 mWakelock.release(); 9119 } 9120 } 9121 } 9122 9123 private void dataChangedImpl(String packageName) { 9124 HashSet<String> targets = dataChangedTargets(packageName); 9125 dataChangedImpl(packageName, targets); 9126 } 9127 9128 private void dataChangedImpl(String packageName, HashSet<String> targets) { 9129 // Record that we need a backup pass for the caller. Since multiple callers 9130 // may share a uid, we need to note all candidates within that uid and schedule 9131 // a backup pass for each of them. 9132 if (targets == null) { 9133 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 9134 + " uid=" + Binder.getCallingUid()); 9135 return; 9136 } 9137 9138 synchronized (mQueueLock) { 9139 // Note that this client has made data changes that need to be backed up 9140 if (targets.contains(packageName)) { 9141 // Add the caller to the set of pending backups. If there is 9142 // one already there, then overwrite it, but no harm done. 9143 BackupRequest req = new BackupRequest(packageName); 9144 if (mPendingBackups.put(packageName, req) == null) { 9145 if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName); 9146 9147 // Journal this request in case of crash. The put() 9148 // operation returned null when this package was not already 9149 // in the set; we want to avoid touching the disk redundantly. 9150 writeToJournalLocked(packageName); 9151 } 9152 } 9153 } 9154 9155 // ...and schedule a backup pass if necessary 9156 KeyValueBackupJob.schedule(mContext); 9157 } 9158 9159 // Note: packageName is currently unused, but may be in the future 9160 private HashSet<String> dataChangedTargets(String packageName) { 9161 // If the caller does not hold the BACKUP permission, it can only request a 9162 // backup of its own data. 9163 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 9164 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 9165 synchronized (mBackupParticipants) { 9166 return mBackupParticipants.get(Binder.getCallingUid()); 9167 } 9168 } 9169 9170 // a caller with full permission can ask to back up any participating app 9171 HashSet<String> targets = new HashSet<String>(); 9172 if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) { 9173 targets.add(PACKAGE_MANAGER_SENTINEL); 9174 } else { 9175 synchronized (mBackupParticipants) { 9176 int N = mBackupParticipants.size(); 9177 for (int i = 0; i < N; i++) { 9178 HashSet<String> s = mBackupParticipants.valueAt(i); 9179 if (s != null) { 9180 targets.addAll(s); 9181 } 9182 } 9183 } 9184 } 9185 return targets; 9186 } 9187 9188 private void writeToJournalLocked(String str) { 9189 RandomAccessFile out = null; 9190 try { 9191 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir); 9192 out = new RandomAccessFile(mJournal, "rws"); 9193 out.seek(out.length()); 9194 out.writeUTF(str); 9195 } catch (IOException e) { 9196 Slog.e(TAG, "Can't write " + str + " to backup journal", e); 9197 mJournal = null; 9198 } finally { 9199 try { if (out != null) out.close(); } catch (IOException e) {} 9200 } 9201 } 9202 9203 // ----- IBackupManager binder interface ----- 9204 9205 public void dataChanged(final String packageName) { 9206 final int callingUserHandle = UserHandle.getCallingUserId(); 9207 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9208 // TODO: http://b/22388012 9209 // App is running under a non-owner user profile. For now, we do not back 9210 // up data from secondary user profiles. 9211 // TODO: backups for all user profiles although don't add backup for profiles 9212 // without adding admin control in DevicePolicyManager. 9213 if (MORE_DEBUG) { 9214 Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user " 9215 + callingUserHandle); 9216 } 9217 return; 9218 } 9219 9220 final HashSet<String> targets = dataChangedTargets(packageName); 9221 if (targets == null) { 9222 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'" 9223 + " uid=" + Binder.getCallingUid()); 9224 return; 9225 } 9226 9227 mBackupHandler.post(new Runnable() { 9228 public void run() { 9229 dataChangedImpl(packageName, targets); 9230 } 9231 }); 9232 } 9233 9234 // Clear the given package's backup data from the current transport 9235 public void clearBackupData(String transportName, String packageName) { 9236 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName); 9237 PackageInfo info; 9238 try { 9239 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 9240 } catch (NameNotFoundException e) { 9241 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data"); 9242 return; 9243 } 9244 9245 // If the caller does not hold the BACKUP permission, it can only request a 9246 // wipe of its own backed-up data. 9247 HashSet<String> apps; 9248 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(), 9249 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { 9250 apps = mBackupParticipants.get(Binder.getCallingUid()); 9251 } else { 9252 // a caller with full permission can ask to back up any participating app 9253 // !!! TODO: allow data-clear of ANY app? 9254 if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps"); 9255 apps = new HashSet<String>(); 9256 int N = mBackupParticipants.size(); 9257 for (int i = 0; i < N; i++) { 9258 HashSet<String> s = mBackupParticipants.valueAt(i); 9259 if (s != null) { 9260 apps.addAll(s); 9261 } 9262 } 9263 } 9264 9265 // Is the given app an available participant? 9266 if (apps.contains(packageName)) { 9267 // found it; fire off the clear request 9268 if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process"); 9269 mBackupHandler.removeMessages(MSG_RETRY_CLEAR); 9270 synchronized (mQueueLock) { 9271 final IBackupTransport transport = getTransport(transportName); 9272 if (transport == null) { 9273 // transport is currently unavailable -- make sure to retry 9274 Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR, 9275 new ClearRetryParams(transportName, packageName)); 9276 mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL); 9277 return; 9278 } 9279 long oldId = Binder.clearCallingIdentity(); 9280 mWakelock.acquire(); 9281 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR, 9282 new ClearParams(transport, info)); 9283 mBackupHandler.sendMessage(msg); 9284 Binder.restoreCallingIdentity(oldId); 9285 } 9286 } 9287 } 9288 9289 // Run a backup pass immediately for any applications that have declared 9290 // that they have pending updates. 9291 public void backupNow() { 9292 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow"); 9293 9294 if (mPowerManager.isPowerSaveMode()) { 9295 if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode"); 9296 KeyValueBackupJob.schedule(mContext); // try again in several hours 9297 } else { 9298 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); 9299 synchronized (mQueueLock) { 9300 // Fire the intent that kicks off the whole shebang... 9301 try { 9302 mRunBackupIntent.send(); 9303 } catch (PendingIntent.CanceledException e) { 9304 // should never happen 9305 Slog.e(TAG, "run-backup intent cancelled!"); 9306 } 9307 9308 // ...and cancel any pending scheduled job, because we've just superseded it 9309 KeyValueBackupJob.cancel(mContext); 9310 } 9311 } 9312 } 9313 9314 boolean deviceIsProvisioned() { 9315 final ContentResolver resolver = mContext.getContentResolver(); 9316 return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0); 9317 } 9318 9319 // Run a *full* backup pass for the given packages, writing the resulting data stream 9320 // to the supplied file descriptor. This method is synchronous and does not return 9321 // to the caller until the backup has been completed. 9322 // 9323 // This is the variant used by 'adb backup'; it requires on-screen confirmation 9324 // by the user because it can be used to offload data over untrusted USB. 9325 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, 9326 boolean includeObbs, boolean includeShared, boolean doWidgets, 9327 boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) { 9328 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup"); 9329 9330 final int callingUserHandle = UserHandle.getCallingUserId(); 9331 // TODO: http://b/22388012 9332 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9333 throw new IllegalStateException("Backup supported only for the device owner"); 9334 } 9335 9336 // Validate 9337 if (!doAllApps) { 9338 if (!includeShared) { 9339 // If we're backing up shared data (sdcard or equivalent), then we can run 9340 // without any supplied app names. Otherwise, we'd be doing no work, so 9341 // report the error. 9342 if (pkgList == null || pkgList.length == 0) { 9343 throw new IllegalArgumentException( 9344 "Backup requested but neither shared nor any apps named"); 9345 } 9346 } 9347 } 9348 9349 long oldId = Binder.clearCallingIdentity(); 9350 try { 9351 // Doesn't make sense to do a full backup prior to setup 9352 if (!deviceIsProvisioned()) { 9353 Slog.i(TAG, "Full backup not supported before setup"); 9354 return; 9355 } 9356 9357 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks 9358 + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps 9359 + " system=" + includeSystem + " pkgs=" + pkgList); 9360 Slog.i(TAG, "Beginning full backup..."); 9361 9362 FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs, 9363 includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList); 9364 final int token = generateToken(); 9365 synchronized (mFullConfirmations) { 9366 mFullConfirmations.put(token, params); 9367 } 9368 9369 // start up the confirmation UI 9370 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token); 9371 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { 9372 Slog.e(TAG, "Unable to launch full backup confirmation"); 9373 mFullConfirmations.delete(token); 9374 return; 9375 } 9376 9377 // make sure the screen is lit for the user interaction 9378 mPowerManager.userActivity(SystemClock.uptimeMillis(), 9379 PowerManager.USER_ACTIVITY_EVENT_OTHER, 9380 0); 9381 9382 // start the confirmation countdown 9383 startConfirmationTimeout(token, params); 9384 9385 // wait for the backup to be performed 9386 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion..."); 9387 waitForCompletion(params); 9388 } finally { 9389 try { 9390 fd.close(); 9391 } catch (IOException e) { 9392 // just eat it 9393 } 9394 Binder.restoreCallingIdentity(oldId); 9395 Slog.d(TAG, "Full backup processing complete."); 9396 } 9397 } 9398 9399 public void fullTransportBackup(String[] pkgNames) { 9400 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, 9401 "fullTransportBackup"); 9402 9403 final int callingUserHandle = UserHandle.getCallingUserId(); 9404 // TODO: http://b/22388012 9405 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9406 throw new IllegalStateException("Restore supported only for the device owner"); 9407 } 9408 9409 if (!fullBackupAllowable(getTransport(mCurrentTransport))) { 9410 Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?"); 9411 } else { 9412 if (DEBUG) { 9413 Slog.d(TAG, "fullTransportBackup()"); 9414 } 9415 9416 final long oldId = Binder.clearCallingIdentity(); 9417 try { 9418 CountDownLatch latch = new CountDownLatch(1); 9419 PerformFullTransportBackupTask task = new PerformFullTransportBackupTask(null, 9420 pkgNames, false, null, latch, null, false /* userInitiated */); 9421 // Acquiring wakelock for PerformFullTransportBackupTask before its start. 9422 mWakelock.acquire(); 9423 (new Thread(task, "full-transport-master")).start(); 9424 do { 9425 try { 9426 latch.await(); 9427 break; 9428 } catch (InterruptedException e) { 9429 // Just go back to waiting for the latch to indicate completion 9430 } 9431 } while (true); 9432 9433 // We just ran a backup on these packages, so kick them to the end of the queue 9434 final long now = System.currentTimeMillis(); 9435 for (String pkg : pkgNames) { 9436 enqueueFullBackup(pkg, now); 9437 } 9438 } finally { 9439 Binder.restoreCallingIdentity(oldId); 9440 } 9441 } 9442 9443 if (DEBUG) { 9444 Slog.d(TAG, "Done with full transport backup."); 9445 } 9446 } 9447 9448 public void fullRestore(ParcelFileDescriptor fd) { 9449 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore"); 9450 9451 final int callingUserHandle = UserHandle.getCallingUserId(); 9452 // TODO: http://b/22388012 9453 if (callingUserHandle != UserHandle.USER_SYSTEM) { 9454 throw new IllegalStateException("Restore supported only for the device owner"); 9455 } 9456 9457 long oldId = Binder.clearCallingIdentity(); 9458 9459 try { 9460 // Check whether the device has been provisioned -- we don't handle 9461 // full restores prior to completing the setup process. 9462 if (!deviceIsProvisioned()) { 9463 Slog.i(TAG, "Full restore not permitted before setup"); 9464 return; 9465 } 9466 9467 Slog.i(TAG, "Beginning full restore..."); 9468 9469 FullRestoreParams params = new FullRestoreParams(fd); 9470 final int token = generateToken(); 9471 synchronized (mFullConfirmations) { 9472 mFullConfirmations.put(token, params); 9473 } 9474 9475 // start up the confirmation UI 9476 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token); 9477 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) { 9478 Slog.e(TAG, "Unable to launch full restore confirmation"); 9479 mFullConfirmations.delete(token); 9480 return; 9481 } 9482 9483 // make sure the screen is lit for the user interaction 9484 mPowerManager.userActivity(SystemClock.uptimeMillis(), 9485 PowerManager.USER_ACTIVITY_EVENT_OTHER, 9486 0); 9487 9488 // start the confirmation countdown 9489 startConfirmationTimeout(token, params); 9490 9491 // wait for the restore to be performed 9492 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion..."); 9493 waitForCompletion(params); 9494 } finally { 9495 try { 9496 fd.close(); 9497 } catch (IOException e) { 9498 Slog.w(TAG, "Error trying to close fd after full restore: " + e); 9499 } 9500 Binder.restoreCallingIdentity(oldId); 9501 Slog.i(TAG, "Full restore processing complete."); 9502 } 9503 } 9504 9505 boolean startConfirmationUi(int token, String action) { 9506 try { 9507 Intent confIntent = new Intent(action); 9508 confIntent.setClassName("com.android.backupconfirm", 9509 "com.android.backupconfirm.BackupRestoreConfirmation"); 9510 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token); 9511 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 9512 mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM); 9513 } catch (ActivityNotFoundException e) { 9514 return false; 9515 } 9516 return true; 9517 } 9518 9519 void startConfirmationTimeout(int token, FullParams params) { 9520 if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after " 9521 + TIMEOUT_FULL_CONFIRMATION + " millis"); 9522 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT, 9523 token, 0, params); 9524 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION); 9525 } 9526 9527 void waitForCompletion(FullParams params) { 9528 synchronized (params.latch) { 9529 while (params.latch.get() == false) { 9530 try { 9531 params.latch.wait(); 9532 } catch (InterruptedException e) { /* never interrupted */ } 9533 } 9534 } 9535 } 9536 9537 void signalFullBackupRestoreCompletion(FullParams params) { 9538 synchronized (params.latch) { 9539 params.latch.set(true); 9540 params.latch.notifyAll(); 9541 } 9542 } 9543 9544 // Confirm that the previously-requested full backup/restore operation can proceed. This 9545 // is used to require a user-facing disclosure about the operation. 9546 public void acknowledgeFullBackupOrRestore(int token, boolean allow, 9547 String curPassword, String encPpassword, IFullBackupRestoreObserver observer) { 9548 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token 9549 + " allow=" + allow); 9550 9551 // TODO: possibly require not just this signature-only permission, but even 9552 // require that the specific designated confirmation-UI app uid is the caller? 9553 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore"); 9554 9555 long oldId = Binder.clearCallingIdentity(); 9556 try { 9557 9558 FullParams params; 9559 synchronized (mFullConfirmations) { 9560 params = mFullConfirmations.get(token); 9561 if (params != null) { 9562 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params); 9563 mFullConfirmations.delete(token); 9564 9565 if (allow) { 9566 final int verb = params instanceof FullBackupParams 9567 ? MSG_RUN_ADB_BACKUP 9568 : MSG_RUN_ADB_RESTORE; 9569 9570 params.observer = observer; 9571 params.curPassword = curPassword; 9572 9573 params.encryptPassword = encPpassword; 9574 9575 if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb); 9576 mWakelock.acquire(); 9577 Message msg = mBackupHandler.obtainMessage(verb, params); 9578 mBackupHandler.sendMessage(msg); 9579 } else { 9580 Slog.w(TAG, "User rejected full backup/restore operation"); 9581 // indicate completion without having actually transferred any data 9582 signalFullBackupRestoreCompletion(params); 9583 } 9584 } else { 9585 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token"); 9586 } 9587 } 9588 } finally { 9589 Binder.restoreCallingIdentity(oldId); 9590 } 9591 } 9592 9593 private static boolean backupSettingMigrated(int userId) { 9594 File base = new File(Environment.getDataDirectory(), "backup"); 9595 File enableFile = new File(base, BACKUP_ENABLE_FILE); 9596 return enableFile.exists(); 9597 } 9598 9599 private static boolean readBackupEnableState(int userId) { 9600 File base = new File(Environment.getDataDirectory(), "backup"); 9601 File enableFile = new File(base, BACKUP_ENABLE_FILE); 9602 if (enableFile.exists()) { 9603 try (FileInputStream fin = new FileInputStream(enableFile)) { 9604 int state = fin.read(); 9605 return state != 0; 9606 } catch (IOException e) { 9607 // can't read the file; fall through to assume disabled 9608 Slog.e(TAG, "Cannot read enable state; assuming disabled"); 9609 } 9610 } else { 9611 if (DEBUG) { 9612 Slog.i(TAG, "isBackupEnabled() => false due to absent settings file"); 9613 } 9614 } 9615 return false; 9616 } 9617 9618 private static void writeBackupEnableState(boolean enable, int userId) { 9619 File base = new File(Environment.getDataDirectory(), "backup"); 9620 File enableFile = new File(base, BACKUP_ENABLE_FILE); 9621 File stage = new File(base, BACKUP_ENABLE_FILE + "-stage"); 9622 FileOutputStream fout = null; 9623 try { 9624 fout = new FileOutputStream(stage); 9625 fout.write(enable ? 1 : 0); 9626 fout.close(); 9627 stage.renameTo(enableFile); 9628 // will be synced immediately by the try-with-resources call to close() 9629 } catch (IOException|RuntimeException e) { 9630 // Whoops; looks like we're doomed. Roll everything out, disabled, 9631 // including the legacy state. 9632 Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: " 9633 + e.getMessage()); 9634 9635 final ContentResolver r = sInstance.mContext.getContentResolver(); 9636 Settings.Secure.putStringForUser(r, 9637 Settings.Secure.BACKUP_ENABLED, null, userId); 9638 enableFile.delete(); 9639 stage.delete(); 9640 } finally { 9641 IoUtils.closeQuietly(fout); 9642 } 9643 } 9644 9645 // Enable/disable backups 9646 public void setBackupEnabled(boolean enable) { 9647 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9648 "setBackupEnabled"); 9649 9650 Slog.i(TAG, "Backup enabled => " + enable); 9651 9652 long oldId = Binder.clearCallingIdentity(); 9653 try { 9654 boolean wasEnabled = mEnabled; 9655 synchronized (this) { 9656 writeBackupEnableState(enable, UserHandle.USER_SYSTEM); 9657 mEnabled = enable; 9658 } 9659 9660 synchronized (mQueueLock) { 9661 if (enable && !wasEnabled && mProvisioned) { 9662 // if we've just been enabled, start scheduling backup passes 9663 KeyValueBackupJob.schedule(mContext); 9664 scheduleNextFullBackupJob(0); 9665 } else if (!enable) { 9666 // No longer enabled, so stop running backups 9667 if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup"); 9668 9669 KeyValueBackupJob.cancel(mContext); 9670 9671 // This also constitutes an opt-out, so we wipe any data for 9672 // this device from the backend. We start that process with 9673 // an alarm in order to guarantee wakelock states. 9674 if (wasEnabled && mProvisioned) { 9675 // NOTE: we currently flush every registered transport, not just 9676 // the currently-active one. 9677 HashSet<String> allTransports; 9678 synchronized (mTransports) { 9679 allTransports = new HashSet<String>(mTransports.keySet()); 9680 } 9681 // build the set of transports for which we are posting an init 9682 for (String transport : allTransports) { 9683 recordInitPendingLocked(true, transport); 9684 } 9685 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 9686 mRunInitIntent); 9687 } 9688 } 9689 } 9690 } finally { 9691 Binder.restoreCallingIdentity(oldId); 9692 } 9693 } 9694 9695 // Enable/disable automatic restore of app data at install time 9696 public void setAutoRestore(boolean doAutoRestore) { 9697 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9698 "setAutoRestore"); 9699 9700 Slog.i(TAG, "Auto restore => " + doAutoRestore); 9701 9702 final long oldId = Binder.clearCallingIdentity(); 9703 try { 9704 synchronized (this) { 9705 Settings.Secure.putInt(mContext.getContentResolver(), 9706 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0); 9707 mAutoRestore = doAutoRestore; 9708 } 9709 } finally { 9710 Binder.restoreCallingIdentity(oldId); 9711 } 9712 } 9713 9714 // Mark the backup service as having been provisioned 9715 public void setBackupProvisioned(boolean available) { 9716 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9717 "setBackupProvisioned"); 9718 /* 9719 * This is now a no-op; provisioning is simply the device's own setup state. 9720 */ 9721 } 9722 9723 // Report whether the backup mechanism is currently enabled 9724 public boolean isBackupEnabled() { 9725 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled"); 9726 return mEnabled; // no need to synchronize just to read it 9727 } 9728 9729 // Report the name of the currently active transport 9730 public String getCurrentTransport() { 9731 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9732 "getCurrentTransport"); 9733 if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport); 9734 return mCurrentTransport; 9735 } 9736 9737 // Report all known, available backup transports 9738 public String[] listAllTransports() { 9739 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports"); 9740 9741 String[] list = null; 9742 ArrayList<String> known = new ArrayList<String>(); 9743 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) { 9744 if (entry.getValue() != null) { 9745 known.add(entry.getKey()); 9746 } 9747 } 9748 9749 if (known.size() > 0) { 9750 list = new String[known.size()]; 9751 known.toArray(list); 9752 } 9753 return list; 9754 } 9755 9756 public String[] getTransportWhitelist() { 9757 // No permission check, intentionally. 9758 String[] whitelist = new String[mTransportWhitelist.size()]; 9759 for (int i = mTransportWhitelist.size() - 1; i >= 0; i--) { 9760 whitelist[i] = mTransportWhitelist.valueAt(i).flattenToShortString(); 9761 } 9762 return whitelist; 9763 } 9764 9765 // Select which transport to use for the next backup operation. 9766 public String selectBackupTransport(String transport) { 9767 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9768 "selectBackupTransport"); 9769 9770 synchronized (mTransports) { 9771 final long oldId = Binder.clearCallingIdentity(); 9772 try { 9773 String prevTransport = mCurrentTransport; 9774 mCurrentTransport = transport; 9775 Settings.Secure.putString(mContext.getContentResolver(), 9776 Settings.Secure.BACKUP_TRANSPORT, transport); 9777 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport 9778 + " returning " + prevTransport); 9779 return prevTransport; 9780 } finally { 9781 Binder.restoreCallingIdentity(oldId); 9782 } 9783 } 9784 } 9785 9786 // Supply the configuration Intent for the given transport. If the name is not one 9787 // of the available transports, or if the transport does not supply any configuration 9788 // UI, the method returns null. 9789 public Intent getConfigurationIntent(String transportName) { 9790 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9791 "getConfigurationIntent"); 9792 9793 synchronized (mTransports) { 9794 final IBackupTransport transport = mTransports.get(transportName); 9795 if (transport != null) { 9796 try { 9797 final Intent intent = transport.configurationIntent(); 9798 if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent " 9799 + intent); 9800 return intent; 9801 } catch (Exception e) { 9802 /* fall through to return null */ 9803 Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage()); 9804 } 9805 } 9806 } 9807 9808 return null; 9809 } 9810 9811 // Supply the configuration summary string for the given transport. If the name is 9812 // not one of the available transports, or if the transport does not supply any 9813 // summary / destination string, the method can return null. 9814 // 9815 // This string is used VERBATIM as the summary text of the relevant Settings item! 9816 public String getDestinationString(String transportName) { 9817 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9818 "getDestinationString"); 9819 9820 synchronized (mTransports) { 9821 final IBackupTransport transport = mTransports.get(transportName); 9822 if (transport != null) { 9823 try { 9824 final String text = transport.currentDestinationString(); 9825 if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text); 9826 return text; 9827 } catch (Exception e) { 9828 /* fall through to return null */ 9829 Slog.e(TAG, "Unable to get string from transport: " + e.getMessage()); 9830 } 9831 } 9832 } 9833 9834 return null; 9835 } 9836 9837 // Supply the manage-data intent for the given transport. 9838 public Intent getDataManagementIntent(String transportName) { 9839 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9840 "getDataManagementIntent"); 9841 9842 synchronized (mTransports) { 9843 final IBackupTransport transport = mTransports.get(transportName); 9844 if (transport != null) { 9845 try { 9846 final Intent intent = transport.dataManagementIntent(); 9847 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent " 9848 + intent); 9849 return intent; 9850 } catch (Exception e) { 9851 /* fall through to return null */ 9852 Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage()); 9853 } 9854 } 9855 } 9856 9857 return null; 9858 } 9859 9860 // Supply the menu label for affordances that fire the manage-data intent 9861 // for the given transport. 9862 public String getDataManagementLabel(String transportName) { 9863 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 9864 "getDataManagementLabel"); 9865 9866 synchronized (mTransports) { 9867 final IBackupTransport transport = mTransports.get(transportName); 9868 if (transport != null) { 9869 try { 9870 final String text = transport.dataManagementLabel(); 9871 if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text); 9872 return text; 9873 } catch (Exception e) { 9874 /* fall through to return null */ 9875 Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage()); 9876 } 9877 } 9878 } 9879 9880 return null; 9881 } 9882 9883 // Callback: a requested backup agent has been instantiated. This should only 9884 // be called from the Activity Manager. 9885 public void agentConnected(String packageName, IBinder agentBinder) { 9886 synchronized(mAgentConnectLock) { 9887 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 9888 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); 9889 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder); 9890 mConnectedAgent = agent; 9891 mConnecting = false; 9892 } else { 9893 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9894 + " claiming agent connected"); 9895 } 9896 mAgentConnectLock.notifyAll(); 9897 } 9898 } 9899 9900 // Callback: a backup agent has failed to come up, or has unexpectedly quit. 9901 // If the agent failed to come up in the first place, the agentBinder argument 9902 // will be null. This should only be called from the Activity Manager. 9903 public void agentDisconnected(String packageName) { 9904 // TODO: handle backup being interrupted 9905 synchronized(mAgentConnectLock) { 9906 if (Binder.getCallingUid() == Process.SYSTEM_UID) { 9907 mConnectedAgent = null; 9908 mConnecting = false; 9909 } else { 9910 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9911 + " claiming agent disconnected"); 9912 } 9913 mAgentConnectLock.notifyAll(); 9914 } 9915 } 9916 9917 // An application being installed will need a restore pass, then the Package Manager 9918 // will need to be told when the restore is finished. 9919 public void restoreAtInstall(String packageName, int token) { 9920 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 9921 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid() 9922 + " attemping install-time restore"); 9923 return; 9924 } 9925 9926 boolean skip = false; 9927 9928 long restoreSet = getAvailableRestoreToken(packageName); 9929 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName 9930 + " token=" + Integer.toHexString(token) 9931 + " restoreSet=" + Long.toHexString(restoreSet)); 9932 if (restoreSet == 0) { 9933 if (MORE_DEBUG) Slog.i(TAG, "No restore set"); 9934 skip = true; 9935 } 9936 9937 // Do we have a transport to fetch data for us? 9938 IBackupTransport transport = getTransport(mCurrentTransport); 9939 if (transport == null) { 9940 if (DEBUG) Slog.w(TAG, "No transport"); 9941 skip = true; 9942 } 9943 9944 if (!mAutoRestore) { 9945 if (DEBUG) { 9946 Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore); 9947 } 9948 skip = true; 9949 } 9950 9951 if (!skip) { 9952 try { 9953 // okay, we're going to attempt a restore of this package from this restore set. 9954 // The eventual message back into the Package Manager to run the post-install 9955 // steps for 'token' will be issued from the restore handling code. 9956 9957 // This can throw and so *must* happen before the wakelock is acquired 9958 String dirName = transport.transportDirName(); 9959 9960 mWakelock.acquire(); 9961 if (MORE_DEBUG) { 9962 Slog.d(TAG, "Restore at install of " + packageName); 9963 } 9964 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 9965 msg.obj = new RestoreParams(transport, dirName, null, 9966 restoreSet, packageName, token); 9967 mBackupHandler.sendMessage(msg); 9968 } catch (Exception e) { 9969 // Calling into the transport broke; back off and proceed with the installation. 9970 Slog.e(TAG, "Unable to contact transport: " + e.getMessage()); 9971 skip = true; 9972 } 9973 } 9974 9975 if (skip) { 9976 // Auto-restore disabled or no way to attempt a restore; just tell the Package 9977 // Manager to proceed with the post-install handling for this package. 9978 if (DEBUG) Slog.v(TAG, "Finishing install immediately"); 9979 try { 9980 mPackageManagerBinder.finishPackageInstall(token, false); 9981 } catch (RemoteException e) { /* can't happen */ } 9982 } 9983 } 9984 9985 // Hand off a restore session 9986 public IRestoreSession beginRestoreSession(String packageName, String transport) { 9987 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName 9988 + " transport=" + transport); 9989 9990 boolean needPermission = true; 9991 if (transport == null) { 9992 transport = mCurrentTransport; 9993 9994 if (packageName != null) { 9995 PackageInfo app = null; 9996 try { 9997 app = mPackageManager.getPackageInfo(packageName, 0); 9998 } catch (NameNotFoundException nnf) { 9999 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 10000 throw new IllegalArgumentException("Package " + packageName + " not found"); 10001 } 10002 10003 if (app.applicationInfo.uid == Binder.getCallingUid()) { 10004 // So: using the current active transport, and the caller has asked 10005 // that its own package will be restored. In this narrow use case 10006 // we do not require the caller to hold the permission. 10007 needPermission = false; 10008 } 10009 } 10010 } 10011 10012 if (needPermission) { 10013 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10014 "beginRestoreSession"); 10015 } else { 10016 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed"); 10017 } 10018 10019 synchronized(this) { 10020 if (mActiveRestoreSession != null) { 10021 Slog.i(TAG, "Restore session requested but one already active"); 10022 return null; 10023 } 10024 if (mBackupRunning) { 10025 Slog.i(TAG, "Restore session requested but currently running backups"); 10026 return null; 10027 } 10028 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport); 10029 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); 10030 } 10031 return mActiveRestoreSession; 10032 } 10033 10034 void clearRestoreSession(ActiveRestoreSession currentSession) { 10035 synchronized(this) { 10036 if (currentSession != mActiveRestoreSession) { 10037 Slog.e(TAG, "ending non-current restore session"); 10038 } else { 10039 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout"); 10040 mActiveRestoreSession = null; 10041 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10042 } 10043 } 10044 } 10045 10046 // Note that a currently-active backup agent has notified us that it has 10047 // completed the given outstanding asynchronous backup/restore operation. 10048 public void opComplete(int token, long result) { 10049 if (MORE_DEBUG) { 10050 Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result); 10051 } 10052 Operation op = null; 10053 synchronized (mCurrentOpLock) { 10054 op = mCurrentOperations.get(token); 10055 if (op != null) { 10056 if (op.state == OP_TIMEOUT) { 10057 // The operation already timed out, and this is a late response. Tidy up 10058 // and ignore it; we've already dealt with the timeout. 10059 op = null; 10060 mCurrentOperations.delete(token); 10061 } else { 10062 op.state = OP_ACKNOWLEDGED; 10063 } 10064 } 10065 mCurrentOpLock.notifyAll(); 10066 } 10067 10068 // The completion callback, if any, is invoked on the handler 10069 if (op != null && op.callback != null) { 10070 Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result); 10071 Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult); 10072 mBackupHandler.sendMessage(msg); 10073 } 10074 } 10075 10076 public boolean isAppEligibleForBackup(String packageName) { 10077 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10078 "isAppEligibleForBackup"); 10079 try { 10080 PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 10081 PackageManager.GET_SIGNATURES); 10082 if (!appIsEligibleForBackup(packageInfo.applicationInfo) || 10083 appIsStopped(packageInfo.applicationInfo)) { 10084 return false; 10085 } 10086 IBackupTransport transport = getTransport(mCurrentTransport); 10087 if (transport != null) { 10088 try { 10089 return transport.isAppEligibleForBackup(packageInfo, 10090 appGetsFullBackup(packageInfo)); 10091 } catch (Exception e) { 10092 Slog.e(TAG, "Unable to ask about eligibility: " + e.getMessage()); 10093 } 10094 } 10095 // If transport is not present we couldn't tell that the package is not eligible. 10096 return true; 10097 } catch (NameNotFoundException e) { 10098 return false; 10099 } 10100 } 10101 10102 // ----- Restore session ----- 10103 10104 class ActiveRestoreSession extends IRestoreSession.Stub { 10105 private static final String TAG = "RestoreSession"; 10106 10107 private String mPackageName; 10108 private IBackupTransport mRestoreTransport = null; 10109 RestoreSet[] mRestoreSets = null; 10110 boolean mEnded = false; 10111 boolean mTimedOut = false; 10112 10113 ActiveRestoreSession(String packageName, String transport) { 10114 mPackageName = packageName; 10115 mRestoreTransport = getTransport(transport); 10116 } 10117 10118 public void markTimedOut() { 10119 mTimedOut = true; 10120 } 10121 10122 // --- Binder interface --- 10123 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) { 10124 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10125 "getAvailableRestoreSets"); 10126 if (observer == null) { 10127 throw new IllegalArgumentException("Observer must not be null"); 10128 } 10129 10130 if (mEnded) { 10131 throw new IllegalStateException("Restore session already ended"); 10132 } 10133 10134 if (mTimedOut) { 10135 Slog.i(TAG, "Session already timed out"); 10136 return -1; 10137 } 10138 10139 long oldId = Binder.clearCallingIdentity(); 10140 try { 10141 if (mRestoreTransport == null) { 10142 Slog.w(TAG, "Null transport getting restore sets"); 10143 return -1; 10144 } 10145 10146 // We know we're doing legit work now, so halt the timeout 10147 // until we're done. It gets started again when the result 10148 // comes in. 10149 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10150 10151 // spin off the transport request to our service thread 10152 mWakelock.acquire(); 10153 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS, 10154 new RestoreGetSetsParams(mRestoreTransport, this, observer)); 10155 mBackupHandler.sendMessage(msg); 10156 return 0; 10157 } catch (Exception e) { 10158 Slog.e(TAG, "Error in getAvailableRestoreSets", e); 10159 return -1; 10160 } finally { 10161 Binder.restoreCallingIdentity(oldId); 10162 } 10163 } 10164 10165 public synchronized int restoreAll(long token, IRestoreObserver observer) { 10166 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10167 "performRestore"); 10168 10169 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token) 10170 + " observer=" + observer); 10171 10172 if (mEnded) { 10173 throw new IllegalStateException("Restore session already ended"); 10174 } 10175 10176 if (mTimedOut) { 10177 Slog.i(TAG, "Session already timed out"); 10178 return -1; 10179 } 10180 10181 if (mRestoreTransport == null || mRestoreSets == null) { 10182 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 10183 return -1; 10184 } 10185 10186 if (mPackageName != null) { 10187 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 10188 return -1; 10189 } 10190 10191 String dirName; 10192 try { 10193 dirName = mRestoreTransport.transportDirName(); 10194 } catch (Exception e) { 10195 // Transport went AWOL; fail. 10196 Slog.e(TAG, "Unable to get transport dir for restore: " + e.getMessage()); 10197 return -1; 10198 } 10199 10200 synchronized (mQueueLock) { 10201 for (int i = 0; i < mRestoreSets.length; i++) { 10202 if (token == mRestoreSets[i].token) { 10203 // Real work, so stop the session timeout until we finalize the restore 10204 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10205 10206 long oldId = Binder.clearCallingIdentity(); 10207 mWakelock.acquire(); 10208 if (MORE_DEBUG) { 10209 Slog.d(TAG, "restoreAll() kicking off"); 10210 } 10211 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 10212 msg.obj = new RestoreParams(mRestoreTransport, dirName, 10213 observer, token); 10214 mBackupHandler.sendMessage(msg); 10215 Binder.restoreCallingIdentity(oldId); 10216 return 0; 10217 } 10218 } 10219 } 10220 10221 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 10222 return -1; 10223 } 10224 10225 // Restores of more than a single package are treated as 'system' restores 10226 public synchronized int restoreSome(long token, IRestoreObserver observer, 10227 String[] packages) { 10228 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, 10229 "performRestore"); 10230 10231 if (DEBUG) { 10232 StringBuilder b = new StringBuilder(128); 10233 b.append("restoreSome token="); 10234 b.append(Long.toHexString(token)); 10235 b.append(" observer="); 10236 b.append(observer.toString()); 10237 b.append(" packages="); 10238 if (packages == null) { 10239 b.append("null"); 10240 } else { 10241 b.append('{'); 10242 boolean first = true; 10243 for (String s : packages) { 10244 if (!first) { 10245 b.append(", "); 10246 } else first = false; 10247 b.append(s); 10248 } 10249 b.append('}'); 10250 } 10251 Slog.d(TAG, b.toString()); 10252 } 10253 10254 if (mEnded) { 10255 throw new IllegalStateException("Restore session already ended"); 10256 } 10257 10258 if (mTimedOut) { 10259 Slog.i(TAG, "Session already timed out"); 10260 return -1; 10261 } 10262 10263 if (mRestoreTransport == null || mRestoreSets == null) { 10264 Slog.e(TAG, "Ignoring restoreAll() with no restore set"); 10265 return -1; 10266 } 10267 10268 if (mPackageName != null) { 10269 Slog.e(TAG, "Ignoring restoreAll() on single-package session"); 10270 return -1; 10271 } 10272 10273 String dirName; 10274 try { 10275 dirName = mRestoreTransport.transportDirName(); 10276 } catch (Exception e) { 10277 // Transport went AWOL; fail. 10278 Slog.e(TAG, "Unable to get transport name for restoreSome: " + e.getMessage()); 10279 return -1; 10280 } 10281 10282 synchronized (mQueueLock) { 10283 for (int i = 0; i < mRestoreSets.length; i++) { 10284 if (token == mRestoreSets[i].token) { 10285 // Stop the session timeout until we finalize the restore 10286 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10287 10288 long oldId = Binder.clearCallingIdentity(); 10289 mWakelock.acquire(); 10290 if (MORE_DEBUG) { 10291 Slog.d(TAG, "restoreSome() of " + packages.length + " packages"); 10292 } 10293 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 10294 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, 10295 packages, packages.length > 1); 10296 mBackupHandler.sendMessage(msg); 10297 Binder.restoreCallingIdentity(oldId); 10298 return 0; 10299 } 10300 } 10301 } 10302 10303 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found"); 10304 return -1; 10305 } 10306 10307 public synchronized int restorePackage(String packageName, IRestoreObserver observer) { 10308 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer); 10309 10310 if (mEnded) { 10311 throw new IllegalStateException("Restore session already ended"); 10312 } 10313 10314 if (mTimedOut) { 10315 Slog.i(TAG, "Session already timed out"); 10316 return -1; 10317 } 10318 10319 if (mPackageName != null) { 10320 if (! mPackageName.equals(packageName)) { 10321 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName 10322 + " on session for package " + mPackageName); 10323 return -1; 10324 } 10325 } 10326 10327 PackageInfo app = null; 10328 try { 10329 app = mPackageManager.getPackageInfo(packageName, 0); 10330 } catch (NameNotFoundException nnf) { 10331 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName); 10332 return -1; 10333 } 10334 10335 // If the caller is not privileged and is not coming from the target 10336 // app's uid, throw a permission exception back to the caller. 10337 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP, 10338 Binder.getCallingPid(), Binder.getCallingUid()); 10339 if ((perm == PackageManager.PERMISSION_DENIED) && 10340 (app.applicationInfo.uid != Binder.getCallingUid())) { 10341 Slog.w(TAG, "restorePackage: bad packageName=" + packageName 10342 + " or calling uid=" + Binder.getCallingUid()); 10343 throw new SecurityException("No permission to restore other packages"); 10344 } 10345 10346 // So far so good; we're allowed to try to restore this package. 10347 long oldId = Binder.clearCallingIdentity(); 10348 try { 10349 // Check whether there is data for it in the current dataset, falling back 10350 // to the ancestral dataset if not. 10351 long token = getAvailableRestoreToken(packageName); 10352 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName 10353 + " token=" + Long.toHexString(token)); 10354 10355 // If we didn't come up with a place to look -- no ancestral dataset and 10356 // the app has never been backed up from this device -- there's nothing 10357 // to do but return failure. 10358 if (token == 0) { 10359 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring"); 10360 return -1; 10361 } 10362 10363 String dirName; 10364 try { 10365 dirName = mRestoreTransport.transportDirName(); 10366 } catch (Exception e) { 10367 // Transport went AWOL; fail. 10368 Slog.e(TAG, "Unable to get transport dir for restorePackage: " + e.getMessage()); 10369 return -1; 10370 } 10371 10372 // Stop the session timeout until we finalize the restore 10373 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); 10374 10375 // Ready to go: enqueue the restore request and claim success 10376 mWakelock.acquire(); 10377 if (MORE_DEBUG) { 10378 Slog.d(TAG, "restorePackage() : " + packageName); 10379 } 10380 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); 10381 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app); 10382 mBackupHandler.sendMessage(msg); 10383 } finally { 10384 Binder.restoreCallingIdentity(oldId); 10385 } 10386 return 0; 10387 } 10388 10389 // Posted to the handler to tear down a restore session in a cleanly synchronized way 10390 class EndRestoreRunnable implements Runnable { 10391 BackupManagerService mBackupManager; 10392 ActiveRestoreSession mSession; 10393 10394 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) { 10395 mBackupManager = manager; 10396 mSession = session; 10397 } 10398 10399 public void run() { 10400 // clean up the session's bookkeeping 10401 synchronized (mSession) { 10402 mSession.mRestoreTransport = null; 10403 mSession.mEnded = true; 10404 } 10405 10406 // clean up the BackupManagerImpl side of the bookkeeping 10407 // and cancel any pending timeout message 10408 mBackupManager.clearRestoreSession(mSession); 10409 } 10410 } 10411 10412 public synchronized void endRestoreSession() { 10413 if (DEBUG) Slog.d(TAG, "endRestoreSession"); 10414 10415 if (mTimedOut) { 10416 Slog.i(TAG, "Session already timed out"); 10417 return; 10418 } 10419 10420 if (mEnded) { 10421 throw new IllegalStateException("Restore session already ended"); 10422 } 10423 10424 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this)); 10425 } 10426 } 10427 10428 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 10429 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 10430 10431 long identityToken = Binder.clearCallingIdentity(); 10432 try { 10433 if (args != null) { 10434 for (String arg : args) { 10435 if ("-h".equals(arg)) { 10436 pw.println("'dumpsys backup' optional arguments:"); 10437 pw.println(" -h : this help text"); 10438 pw.println(" a[gents] : dump information about defined backup agents"); 10439 return; 10440 } else if ("agents".startsWith(arg)) { 10441 dumpAgents(pw); 10442 return; 10443 } 10444 } 10445 } 10446 dumpInternal(pw); 10447 } finally { 10448 Binder.restoreCallingIdentity(identityToken); 10449 } 10450 } 10451 10452 private void dumpAgents(PrintWriter pw) { 10453 List<PackageInfo> agentPackages = allAgentPackages(); 10454 pw.println("Defined backup agents:"); 10455 for (PackageInfo pkg : agentPackages) { 10456 pw.print(" "); 10457 pw.print(pkg.packageName); pw.println(':'); 10458 pw.print(" "); pw.println(pkg.applicationInfo.backupAgentName); 10459 } 10460 } 10461 10462 private void dumpInternal(PrintWriter pw) { 10463 synchronized (mQueueLock) { 10464 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled") 10465 + " / " + (!mProvisioned ? "not " : "") + "provisioned / " 10466 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init"); 10467 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled")); 10468 if (mBackupRunning) pw.println("Backup currently running"); 10469 pw.println("Last backup pass started: " + mLastBackupPass 10470 + " (now = " + System.currentTimeMillis() + ')'); 10471 pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled()); 10472 10473 pw.println("Transport whitelist:"); 10474 for (ComponentName transport : mTransportWhitelist) { 10475 pw.print(" "); 10476 pw.println(transport.flattenToShortString()); 10477 } 10478 10479 pw.println("Available transports:"); 10480 final String[] transports = listAllTransports(); 10481 if (transports != null) { 10482 for (String t : listAllTransports()) { 10483 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t); 10484 try { 10485 IBackupTransport transport = getTransport(t); 10486 File dir = new File(mBaseStateDir, transport.transportDirName()); 10487 pw.println(" destination: " + transport.currentDestinationString()); 10488 pw.println(" intent: " + transport.configurationIntent()); 10489 for (File f : dir.listFiles()) { 10490 pw.println(" " + f.getName() + " - " + f.length() + " state bytes"); 10491 } 10492 } catch (Exception e) { 10493 Slog.e(TAG, "Error in transport", e); 10494 pw.println(" Error: " + e); 10495 } 10496 } 10497 } 10498 10499 pw.println("Pending init: " + mPendingInits.size()); 10500 for (String s : mPendingInits) { 10501 pw.println(" " + s); 10502 } 10503 10504 if (DEBUG_BACKUP_TRACE) { 10505 synchronized (mBackupTrace) { 10506 if (!mBackupTrace.isEmpty()) { 10507 pw.println("Most recent backup trace:"); 10508 for (String s : mBackupTrace) { 10509 pw.println(" " + s); 10510 } 10511 } 10512 } 10513 } 10514 10515 pw.print("Ancestral: "); pw.println(Long.toHexString(mAncestralToken)); 10516 pw.print("Current: "); pw.println(Long.toHexString(mCurrentToken)); 10517 10518 int N = mBackupParticipants.size(); 10519 pw.println("Participants:"); 10520 for (int i=0; i<N; i++) { 10521 int uid = mBackupParticipants.keyAt(i); 10522 pw.print(" uid: "); 10523 pw.println(uid); 10524 HashSet<String> participants = mBackupParticipants.valueAt(i); 10525 for (String app: participants) { 10526 pw.println(" " + app); 10527 } 10528 } 10529 10530 pw.println("Ancestral packages: " 10531 + (mAncestralPackages == null ? "none" : mAncestralPackages.size())); 10532 if (mAncestralPackages != null) { 10533 for (String pkg : mAncestralPackages) { 10534 pw.println(" " + pkg); 10535 } 10536 } 10537 10538 pw.println("Ever backed up: " + mEverStoredApps.size()); 10539 for (String pkg : mEverStoredApps) { 10540 pw.println(" " + pkg); 10541 } 10542 10543 pw.println("Pending key/value backup: " + mPendingBackups.size()); 10544 for (BackupRequest req : mPendingBackups.values()) { 10545 pw.println(" " + req); 10546 } 10547 10548 pw.println("Full backup queue:" + mFullBackupQueue.size()); 10549 for (FullBackupEntry entry : mFullBackupQueue) { 10550 pw.print(" "); pw.print(entry.lastBackup); 10551 pw.print(" : "); pw.println(entry.packageName); 10552 } 10553 } 10554 } 10555 10556 private static void sendBackupOnUpdate(IBackupObserver observer, String packageName, 10557 BackupProgress progress) { 10558 if (observer != null) { 10559 try { 10560 observer.onUpdate(packageName, progress); 10561 } catch (RemoteException e) { 10562 if (DEBUG) { 10563 Slog.w(TAG, "Backup observer went away: onUpdate"); 10564 } 10565 } 10566 } 10567 } 10568 10569 private static void sendBackupOnPackageResult(IBackupObserver observer, String packageName, 10570 int status) { 10571 if (observer != null) { 10572 try { 10573 observer.onResult(packageName, status); 10574 } catch (RemoteException e) { 10575 if (DEBUG) { 10576 Slog.w(TAG, "Backup observer went away: onResult"); 10577 } 10578 } 10579 } 10580 } 10581 10582 private static void sendBackupFinished(IBackupObserver observer, int status) { 10583 if (observer != null) { 10584 try { 10585 observer.backupFinished(status); 10586 } catch (RemoteException e) { 10587 if (DEBUG) { 10588 Slog.w(TAG, "Backup observer went away: backupFinished"); 10589 } 10590 } 10591 } 10592 } 10593 } 10594