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