1 /* 2 * Copyright (C) 2018 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.rollback; 18 19 import static android.crashrecovery.flags.Flags.deprecateFlagsAndSettingsResets; 20 21 import static com.android.server.rollback.RollbackManagerServiceImpl.sendFailure; 22 23 import android.Manifest; 24 import android.annotation.AnyThread; 25 import android.annotation.IntDef; 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.annotation.WorkerThread; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentSender; 32 import android.content.pm.PackageInstaller; 33 import android.content.pm.PackageManager; 34 import android.content.pm.PackageManagerInternal; 35 import android.content.pm.VersionedPackage; 36 import android.content.rollback.PackageRollbackInfo; 37 import android.content.rollback.RollbackInfo; 38 import android.content.rollback.RollbackManager; 39 import android.os.Binder; 40 import android.os.Handler; 41 import android.os.Looper; 42 import android.os.ParcelFileDescriptor; 43 import android.os.UserHandle; 44 import android.os.UserManager; 45 import android.os.ext.SdkExtensions; 46 import android.text.TextUtils; 47 import android.util.ArraySet; 48 import android.util.Slog; 49 import android.util.SparseIntArray; 50 51 import com.android.internal.annotations.VisibleForTesting; 52 import com.android.internal.util.ArrayUtils; 53 import com.android.internal.util.IndentingPrintWriter; 54 import com.android.internal.util.Preconditions; 55 import com.android.server.LocalServices; 56 import com.android.server.crashrecovery.CrashRecoveryAdaptor; 57 import com.android.server.pm.pkg.AndroidPackage; 58 59 import java.io.File; 60 import java.io.IOException; 61 import java.lang.annotation.Retention; 62 import java.lang.annotation.RetentionPolicy; 63 import java.text.ParseException; 64 import java.time.Instant; 65 import java.util.ArrayList; 66 import java.util.List; 67 import java.util.Objects; 68 import java.util.Set; 69 import java.util.function.Consumer; 70 71 /** 72 * Information about a rollback available for a set of atomically installed packages. 73 * 74 * Threading model: 75 * 76 * Each method falls into one of the 2 categories: 77 * - @AnyThread annotates thread-safe methods. 78 * - @WorkerThread annotates methods that should be called from the worker thread only. 79 * 80 * In production code, the constructor is called on the worker thread of 81 * {@link RollbackManagerServiceImpl}. All method invocations should happen on this thread. 82 * Violation of thread invariants will trigger exceptions. In the case of unit tests, it is up to 83 * the tests to serialize all method calls to avoid race condition. No thread invariants are 84 * enforced in this case. 85 */ 86 class Rollback { 87 88 private static final String TAG = "RollbackManager"; 89 90 @IntDef(prefix = { "ROLLBACK_STATE_" }, value = { 91 ROLLBACK_STATE_ENABLING, 92 ROLLBACK_STATE_AVAILABLE, 93 ROLLBACK_STATE_COMMITTED, 94 ROLLBACK_STATE_DELETED 95 }) 96 @Retention(RetentionPolicy.SOURCE) 97 @interface RollbackState {} 98 99 /** 100 * The rollback is in the process of being enabled. It is not yet 101 * available for use. 102 */ 103 static final int ROLLBACK_STATE_ENABLING = 0; 104 105 /** 106 * The rollback is currently available. 107 */ 108 static final int ROLLBACK_STATE_AVAILABLE = 1; 109 110 /** 111 * The rollback has been committed. 112 */ 113 static final int ROLLBACK_STATE_COMMITTED = 3; 114 115 /** 116 * The rollback has been deleted. 117 */ 118 static final int ROLLBACK_STATE_DELETED = 4; 119 120 /** 121 * The session ID associate with this rollback. This is the parent session ID in the case of 122 * a multi-package session. 123 */ 124 private final int mOriginalSessionId; 125 126 /** 127 * The rollback info for this rollback. 128 */ 129 public final RollbackInfo info; 130 131 /** 132 * The directory where the rollback data is stored. 133 */ 134 private final File mBackupDir; 135 136 /** 137 * The time when the upgrade occurred, for purposes of expiring 138 * rollback data. 139 * 140 * The timestamp is not applicable for all rollback states, but we make 141 * sure to keep it non-null to avoid potential errors there. 142 */ 143 private @NonNull Instant mTimestamp; 144 145 /** 146 * The current state of the rollback. 147 * ENABLING, AVAILABLE, DELETED, or COMMITTED. 148 */ 149 private @RollbackState int mState; 150 151 /** 152 * The detailed description of the current state. For a DELETED state, it describes 153 * the reason why the rollback is deleted. 154 */ 155 private @NonNull String mStateDescription = ""; 156 157 /** 158 * True if we are expecting the package manager to call restoreUserData 159 * for this rollback because it has just been committed but the rollback 160 * has not yet been fully applied. 161 */ 162 private boolean mRestoreUserDataInProgress = false; 163 164 /** 165 * The user that performed the install with rollback enabled. 166 */ 167 private final int mUserId; 168 169 /** 170 * The installer package name from the install session that enabled the rollback. May be null if 171 * that session did not set this value. 172 * 173 * If this is an empty string then the installer package name will be resolved by 174 * PackageManager. 175 */ 176 @Nullable private final String mInstallerPackageName; 177 178 /** 179 * Time after which rollback expires. 180 */ 181 private long mRollbackLifetimeMillis = 0; 182 183 /** 184 * Session ids for all packages in the install. For multi-package sessions, this is the list 185 * of child session ids. For normal sessions, this list is a single element with the normal 186 * session id. 187 */ 188 private final int[] mPackageSessionIds; 189 190 /** 191 * The extension versions supported at the time of rollback creation. 192 */ 193 @NonNull private final SparseIntArray mExtensionVersions; 194 195 /** 196 * The worker thread on which all method invocations should happen. It might be null in the 197 * case of unit tests where no thread invariants are enforced. 198 */ 199 @Nullable private final Handler mHandler; 200 201 /** 202 * Constructs a new, empty Rollback instance. 203 * 204 * @param rollbackId the id of the rollback. 205 * @param backupDir the directory where the rollback data is stored. 206 * @param originalSessionId the session id associated with this rollback. 207 * @param isStaged true if this is a staged rollback. 208 * @param userId the user that performed the install with rollback enabled. 209 * @param installerPackageName the installer package name from the original install session. 210 * @param packageSessionIds the session ids for all packages in the install. 211 * @param extensionVersions the extension versions supported at the time of rollback creation 212 */ Rollback(int rollbackId, File backupDir, int originalSessionId, boolean isStaged, int userId, String installerPackageName, int[] packageSessionIds, SparseIntArray extensionVersions)213 Rollback(int rollbackId, File backupDir, int originalSessionId, boolean isStaged, int userId, 214 String installerPackageName, int[] packageSessionIds, 215 SparseIntArray extensionVersions) { 216 this.info = new RollbackInfo(rollbackId, 217 /* packages */ new ArrayList<>(), 218 /* isStaged */ isStaged, 219 /* causePackages */ new ArrayList<>(), 220 /* committedSessionId */ -1, 221 /* rollbackImpactLevel */ PackageManager.ROLLBACK_USER_IMPACT_LOW); 222 mUserId = userId; 223 mInstallerPackageName = installerPackageName; 224 mBackupDir = backupDir; 225 mOriginalSessionId = originalSessionId; 226 mState = ROLLBACK_STATE_ENABLING; 227 mTimestamp = Instant.now(); 228 mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0]; 229 mExtensionVersions = Objects.requireNonNull(extensionVersions); 230 mHandler = Looper.myLooper() != null ? new Handler(Looper.myLooper()) : null; 231 } 232 233 /** 234 * Constructs a pre-populated Rollback instance. 235 */ Rollback(RollbackInfo info, File backupDir, Instant timestamp, int originalSessionId, @RollbackState int state, String stateDescription, boolean restoreUserDataInProgress, int userId, String installerPackageName, SparseIntArray extensionVersions)236 Rollback(RollbackInfo info, File backupDir, Instant timestamp, int originalSessionId, 237 @RollbackState int state, String stateDescription, boolean restoreUserDataInProgress, 238 int userId, String installerPackageName, SparseIntArray extensionVersions) { 239 this.info = info; 240 mUserId = userId; 241 mInstallerPackageName = installerPackageName; 242 mBackupDir = backupDir; 243 mTimestamp = timestamp; 244 mOriginalSessionId = originalSessionId; 245 mState = state; 246 mStateDescription = stateDescription; 247 mRestoreUserDataInProgress = restoreUserDataInProgress; 248 mExtensionVersions = Objects.requireNonNull(extensionVersions); 249 // TODO(b/120200473): Include this field during persistence. This field will be used to 250 // decide which rollback to expire when ACTION_PACKAGE_REPLACED is received. Note persisting 251 // this field is not backward compatible. We won't fix b/120200473 until S to minimize the 252 // impact. 253 mPackageSessionIds = new int[0]; 254 mHandler = Looper.myLooper() != null ? new Handler(Looper.myLooper()) : null; 255 } 256 assertInWorkerThread()257 private void assertInWorkerThread() { 258 Preconditions.checkState(mHandler == null || mHandler.getLooper().isCurrentThread()); 259 } 260 261 /** 262 * Whether the rollback is for rollback of a staged install. 263 */ 264 @AnyThread isStaged()265 boolean isStaged() { 266 return info.isStaged(); 267 } 268 269 /** 270 * Returns the directory in which rollback data should be stored. 271 */ 272 @AnyThread getBackupDir()273 File getBackupDir() { 274 return mBackupDir; 275 } 276 277 /** 278 * Returns the time when the upgrade occurred, for purposes of expiring rollback data. 279 */ 280 @WorkerThread getTimestamp()281 Instant getTimestamp() { 282 assertInWorkerThread(); 283 return mTimestamp; 284 } 285 286 /** 287 * Sets the time at which upgrade occurred. 288 */ 289 @WorkerThread setTimestamp(Instant timestamp)290 void setTimestamp(Instant timestamp) { 291 assertInWorkerThread(); 292 mTimestamp = timestamp; 293 RollbackStore.saveRollback(this); 294 } 295 296 /** 297 * Sets rollback lifetime in milliseconds, for purposes of expiring rollback data. 298 */ 299 @WorkerThread setRollbackLifetimeMillis(long lifetimeMillis)300 void setRollbackLifetimeMillis(long lifetimeMillis) { 301 assertInWorkerThread(); 302 mRollbackLifetimeMillis = lifetimeMillis; 303 } 304 305 /** 306 * Returns rollback lifetime in milliseconds, for purposes of expiring rollback data. 307 */ 308 @WorkerThread getRollbackLifetimeMillis()309 long getRollbackLifetimeMillis() { 310 assertInWorkerThread(); 311 return mRollbackLifetimeMillis; 312 } 313 314 /** 315 * Returns the session ID associated with this rollback, or {@code -1} if unknown. 316 */ 317 @AnyThread getOriginalSessionId()318 int getOriginalSessionId() { 319 return mOriginalSessionId; 320 } 321 322 /** 323 * Returns the ID of the user that performed the install with rollback enabled. 324 */ 325 @AnyThread getUserId()326 int getUserId() { 327 return mUserId; 328 } 329 330 /** 331 * Returns the installer package name from the install session that enabled the rollback. In the 332 * case that this is called on a rollback from an older version, returns the empty string. 333 */ 334 @AnyThread getInstallerPackageName()335 @Nullable String getInstallerPackageName() { 336 return mInstallerPackageName; 337 } 338 339 /** 340 * Returns the extension versions that were supported at the time that the rollback was created, 341 * as a mapping from SdkVersion to ExtensionVersion. 342 */ 343 @AnyThread getExtensionVersions()344 SparseIntArray getExtensionVersions() { 345 return mExtensionVersions; 346 } 347 348 /** 349 * Returns true if the rollback is in the ENABLING state. 350 */ 351 @WorkerThread isEnabling()352 boolean isEnabling() { 353 assertInWorkerThread(); 354 return mState == ROLLBACK_STATE_ENABLING; 355 } 356 357 /** 358 * Returns true if the rollback is in the AVAILABLE state. 359 */ 360 @WorkerThread isAvailable()361 boolean isAvailable() { 362 assertInWorkerThread(); 363 return mState == ROLLBACK_STATE_AVAILABLE; 364 } 365 366 /** 367 * Returns true if the rollback is in the COMMITTED state. 368 */ 369 @WorkerThread isCommitted()370 boolean isCommitted() { 371 assertInWorkerThread(); 372 return mState == ROLLBACK_STATE_COMMITTED; 373 } 374 375 /** 376 * Returns true if the rollback is in the DELETED state. 377 */ 378 @WorkerThread isDeleted()379 boolean isDeleted() { 380 assertInWorkerThread(); 381 return mState == ROLLBACK_STATE_DELETED; 382 } 383 384 /** 385 * Saves this rollback to persistent storage. 386 */ 387 @WorkerThread saveRollback()388 void saveRollback() { 389 assertInWorkerThread(); 390 RollbackStore.saveRollback(this); 391 } 392 393 /** 394 * Enables this rollback for the provided package. 395 * 396 * @return boolean True if the rollback was enabled successfully for the specified package. 397 */ 398 @WorkerThread enableForPackage(String packageName, long newVersion, long installedVersion, boolean isApex, String sourceDir, String[] splitSourceDirs, int rollbackDataPolicy, @PackageManager.RollbackImpactLevel int rollbackImpactLevel)399 boolean enableForPackage(String packageName, long newVersion, long installedVersion, 400 boolean isApex, String sourceDir, String[] splitSourceDirs, int rollbackDataPolicy, 401 @PackageManager.RollbackImpactLevel int rollbackImpactLevel) { 402 assertInWorkerThread(); 403 try { 404 RollbackStore.backupPackageCodePath(this, packageName, sourceDir); 405 if (!ArrayUtils.isEmpty(splitSourceDirs)) { 406 for (String dir : splitSourceDirs) { 407 RollbackStore.backupPackageCodePath(this, packageName, dir); 408 } 409 } 410 } catch (IOException e) { 411 Slog.e(TAG, "Unable to copy package for rollback for " + packageName, e); 412 return false; 413 } 414 415 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo( 416 new VersionedPackage(packageName, newVersion), 417 new VersionedPackage(packageName, installedVersion), 418 new ArrayList<>() /* pendingBackups */, new ArrayList<>() /* pendingRestores */, 419 isApex, false /* isApkInApex */, new ArrayList<>(), rollbackDataPolicy); 420 421 info.getPackages().add(packageRollbackInfo); 422 423 if (info.getRollbackImpactLevel() < rollbackImpactLevel) { 424 info.setRollbackImpactLevel(rollbackImpactLevel); 425 } 426 return true; 427 } 428 429 /** 430 * Enables this rollback for the provided apk-in-apex. 431 * 432 * @return boolean True if the rollback was enabled successfully for the specified package. 433 */ 434 @WorkerThread enableForPackageInApex(String packageName, long installedVersion, int rollbackDataPolicy)435 boolean enableForPackageInApex(String packageName, long installedVersion, 436 int rollbackDataPolicy) { 437 assertInWorkerThread(); 438 // TODO(b/147666157): Extract the new version number of apk-in-apex 439 // The new version for the apk-in-apex is set to 0 for now. If the package is then further 440 // updated via non-staged install flow, then RollbackManagerServiceImpl#onPackageReplaced() 441 // will be called and this rollback will be deleted. Other ways of package update have not 442 // been handled yet. 443 PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo( 444 new VersionedPackage(packageName, 0 /* newVersion */), 445 new VersionedPackage(packageName, installedVersion), 446 new ArrayList<>() /* pendingBackups */, new ArrayList<>() /* pendingRestores */, 447 false /* isApex */, true /* isApkInApex */, new ArrayList<>(), rollbackDataPolicy); 448 info.getPackages().add(packageRollbackInfo); 449 return true; 450 } 451 addAll(List<Integer> list, int[] arr)452 private static void addAll(List<Integer> list, int[] arr) { 453 for (int i = 0; i < arr.length; ++i) { 454 list.add(arr[i]); 455 } 456 } 457 458 /** 459 * Snapshots user data for the provided package and user ids. Does nothing if this rollback is 460 * not in the ENABLING state. 461 */ 462 @WorkerThread snapshotUserData(String packageName, int[] userIds, AppDataRollbackHelper dataHelper)463 void snapshotUserData(String packageName, int[] userIds, AppDataRollbackHelper dataHelper) { 464 assertInWorkerThread(); 465 if (!isEnabling()) { 466 return; 467 } 468 469 for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { 470 if (pkgRollbackInfo.getPackageName().equals(packageName)) { 471 if (pkgRollbackInfo.getRollbackDataPolicy() 472 == PackageManager.ROLLBACK_DATA_POLICY_RESTORE) { 473 dataHelper.snapshotAppData(info.getRollbackId(), pkgRollbackInfo, userIds); 474 addAll(pkgRollbackInfo.getSnapshottedUsers(), userIds); 475 RollbackStore.saveRollback(this); 476 } 477 break; 478 } 479 } 480 } 481 482 /** 483 * Commits the pending backups and restores for a given {@code userId}. If this rollback has a 484 * pending backup, it is updated with a mapping from {@code userId} to inode of the CE user data 485 * snapshot. 486 */ 487 @WorkerThread commitPendingBackupAndRestoreForUser(int userId, AppDataRollbackHelper dataHelper)488 void commitPendingBackupAndRestoreForUser(int userId, AppDataRollbackHelper dataHelper) { 489 assertInWorkerThread(); 490 if (dataHelper.commitPendingBackupAndRestoreForUser(userId, this)) { 491 RollbackStore.saveRollback(this); 492 } 493 } 494 495 /** 496 * Changes the state of the rollback to AVAILABLE. This also changes the timestamp to the 497 * current time and saves the rollback. Does nothing if this rollback is already in the 498 * DELETED state. 499 */ 500 @WorkerThread makeAvailable()501 void makeAvailable() { 502 assertInWorkerThread(); 503 if (isDeleted()) { 504 Slog.w(TAG, "Cannot make deleted rollback available."); 505 return; 506 } 507 setState(ROLLBACK_STATE_AVAILABLE, ""); 508 mTimestamp = Instant.now(); 509 RollbackStore.saveRollback(this); 510 } 511 512 /** 513 * Commits the rollback. 514 */ 515 @WorkerThread commit(final Context context, List<VersionedPackage> causePackages, String callerPackageName, IntentSender statusReceiver)516 void commit(final Context context, List<VersionedPackage> causePackages, 517 String callerPackageName, IntentSender statusReceiver) { 518 assertInWorkerThread(); 519 if (!isAvailable()) { 520 sendFailure(context, statusReceiver, 521 RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, 522 "Rollback unavailable"); 523 return; 524 } 525 526 if (containsApex() && wasCreatedAtLowerExtensionVersion()) { 527 PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); 528 if (extensionVersionReductionWouldViolateConstraint(mExtensionVersions, pmi)) { 529 sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, 530 "Rollback may violate a minExtensionVersion constraint"); 531 return; 532 } 533 } 534 535 // Get a context to use to install the downgraded version of the package. 536 Context pkgContext; 537 try { 538 pkgContext = context.createPackageContextAsUser(callerPackageName, 0, 539 UserHandle.of(mUserId)); 540 } catch (PackageManager.NameNotFoundException e) { 541 sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, 542 "Invalid callerPackageName"); 543 return; 544 } 545 546 PackageManager pm = pkgContext.getPackageManager(); 547 try { 548 PackageInstaller packageInstaller = pm.getPackageInstaller(); 549 PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams( 550 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 551 parentParams.setRequestDowngrade(true); 552 parentParams.setMultiPackage(); 553 if (isStaged()) { 554 parentParams.setStaged(); 555 } 556 parentParams.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK); 557 558 int parentSessionId = packageInstaller.createSession(parentParams); 559 PackageInstaller.Session parentSession = packageInstaller.openSession( 560 parentSessionId); 561 562 List<String> packageNames = new ArrayList<>(info.getPackages().size()); 563 for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { 564 packageNames.add(pkgRollbackInfo.getPackageName()); 565 566 if (pkgRollbackInfo.isApkInApex()) { 567 // No need to issue a downgrade install request for apk-in-apex. It will 568 // be rolled back when its parent apex is downgraded. 569 continue; 570 } 571 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 572 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 573 String installerPackageName = mInstallerPackageName; 574 if (TextUtils.isEmpty(mInstallerPackageName)) { 575 installerPackageName = pm.getInstallerPackageName( 576 pkgRollbackInfo.getPackageName()); 577 } 578 if (installerPackageName != null) { 579 params.setInstallerPackageName(installerPackageName); 580 } 581 params.setRequestDowngrade(true); 582 params.setRequiredInstalledVersionCode( 583 pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode()); 584 params.setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK); 585 if (isStaged()) { 586 params.setStaged(); 587 } 588 if (pkgRollbackInfo.isApex()) { 589 params.setInstallAsApex(); 590 } 591 int sessionId = packageInstaller.createSession(params); 592 PackageInstaller.Session session = packageInstaller.openSession(sessionId); 593 File[] packageCodePaths = RollbackStore.getPackageCodePaths( 594 this, pkgRollbackInfo.getPackageName()); 595 if (packageCodePaths == null) { 596 sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, 597 "Backup copy of package: " 598 + pkgRollbackInfo.getPackageName() + " is inaccessible"); 599 return; 600 } 601 602 for (File packageCodePath : packageCodePaths) { 603 try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath, 604 ParcelFileDescriptor.MODE_READ_ONLY)) { 605 final long token = Binder.clearCallingIdentity(); 606 try { 607 boolean fallbackToCopy = false; 608 try { 609 // Populate apk/apex files using hard links to avoid copy 610 session.stageViaHardLink(packageCodePath.getAbsolutePath()); 611 } catch (Exception ignore) { 612 fallbackToCopy = true; 613 } 614 if (fallbackToCopy) { 615 session.write(packageCodePath.getName(), 0, 616 packageCodePath.length(), 617 fd); 618 } 619 } finally { 620 Binder.restoreCallingIdentity(token); 621 } 622 } 623 } 624 parentSession.addChildSessionId(sessionId); 625 } 626 627 if (!deprecateFlagsAndSettingsResets()) { 628 // Clear flags. 629 CrashRecoveryAdaptor.rescuePartyResetDeviceConfigForPackages(packageNames); 630 } 631 632 Consumer<Intent> onResult = result -> { 633 mHandler.post(() -> { 634 assertInWorkerThread(); 635 int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, 636 PackageInstaller.STATUS_FAILURE); 637 if (status != PackageInstaller.STATUS_SUCCESS) { 638 // Committing the rollback failed, but we still have all the info we 639 // need to try rolling back again, so restore the rollback state to 640 // how it was before we tried committing. 641 // TODO: Should we just kill this rollback if commit failed? 642 // Why would we expect commit not to fail again? 643 // TODO: Could this cause a rollback to be resurrected 644 // if it should otherwise have expired by now? 645 setState(ROLLBACK_STATE_AVAILABLE, "Commit failed"); 646 mRestoreUserDataInProgress = false; 647 info.setCommittedSessionId(-1); 648 sendFailure(context, statusReceiver, 649 RollbackManager.STATUS_FAILURE_INSTALL, 650 "Rollback downgrade install failed: " 651 + result.getStringExtra( 652 PackageInstaller.EXTRA_STATUS_MESSAGE)); 653 return; 654 } 655 656 if (!isStaged()) { 657 // All calls to restoreUserData should have 658 // completed by now for a non-staged install. 659 mRestoreUserDataInProgress = false; 660 } 661 662 info.getCausePackages().addAll(causePackages); 663 RollbackStore.deletePackageCodePaths(this); 664 RollbackStore.saveRollback(this); 665 666 // Send success. 667 try { 668 final Intent fillIn = new Intent(); 669 fillIn.putExtra( 670 RollbackManager.EXTRA_STATUS, 671 RollbackManager.STATUS_SUCCESS); 672 statusReceiver.sendIntent(context, 0, fillIn, null, null); 673 } catch (IntentSender.SendIntentException e) { 674 // Nowhere to send the result back to, so don't bother. 675 } 676 677 Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED); 678 679 UserManager userManager = context.getSystemService(UserManager.class); 680 for (UserHandle user : userManager.getUserHandles(true)) { 681 context.sendBroadcastAsUser(broadcast, 682 user, 683 Manifest.permission.MANAGE_ROLLBACKS); 684 } 685 }); 686 }; 687 688 final LocalIntentReceiver receiver = new LocalIntentReceiver(onResult); 689 setState(ROLLBACK_STATE_COMMITTED, ""); 690 info.setCommittedSessionId(parentSessionId); 691 mRestoreUserDataInProgress = true; 692 parentSession.commit(receiver.getIntentSender()); 693 } catch (IOException e) { 694 Slog.e(TAG, "Rollback failed", e); 695 sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, 696 "IOException: " + e.toString()); 697 } 698 } 699 700 /** 701 * Restores user data for the specified package if this rollback is currently marked as 702 * having a restore in progress. 703 * 704 * @return boolean True if this rollback has a restore in progress and contains the specified 705 * package. 706 */ 707 @WorkerThread restoreUserDataForPackageIfInProgress(String packageName, int[] userIds, int appId, String seInfo, AppDataRollbackHelper dataHelper)708 boolean restoreUserDataForPackageIfInProgress(String packageName, int[] userIds, int appId, 709 String seInfo, AppDataRollbackHelper dataHelper) { 710 assertInWorkerThread(); 711 if (!isRestoreUserDataInProgress()) { 712 return false; 713 } 714 715 boolean foundPackage = false; 716 for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { 717 if (pkgRollbackInfo.getPackageName().equals(packageName)) { 718 foundPackage = true; 719 boolean changedRollback = false; 720 for (int userId : userIds) { 721 changedRollback |= dataHelper.restoreAppData( 722 info.getRollbackId(), pkgRollbackInfo, userId, appId, seInfo); 723 } 724 // We've updated metadata about this rollback, so save it to flash. 725 if (changedRollback) { 726 RollbackStore.saveRollback(this); 727 } 728 break; 729 } 730 } 731 return foundPackage; 732 } 733 734 /** 735 * Deletes app data snapshots associated with this rollback, and moves to the DELETED state. 736 */ 737 @WorkerThread delete(AppDataRollbackHelper dataHelper, @NonNull String reason)738 void delete(AppDataRollbackHelper dataHelper, @NonNull String reason) { 739 assertInWorkerThread(); 740 boolean containsApex = false; 741 Set<Integer> apexUsers = new ArraySet<>(); 742 for (PackageRollbackInfo pkgInfo : info.getPackages()) { 743 List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers(); 744 if (pkgInfo.isApex()) { 745 containsApex = true; 746 apexUsers.addAll(snapshottedUsers); 747 } else { 748 for (int i = 0; i < snapshottedUsers.size(); i++) { 749 // Destroy app data snapshot. 750 int userId = snapshottedUsers.get(i); 751 752 dataHelper.destroyAppDataSnapshot(info.getRollbackId(), pkgInfo, userId); 753 } 754 } 755 } 756 if (containsApex) { 757 dataHelper.destroyApexDeSnapshots(info.getRollbackId()); 758 for (int user : apexUsers) { 759 dataHelper.destroyApexCeSnapshots(user, info.getRollbackId()); 760 } 761 } 762 763 RollbackStore.deleteRollback(this); 764 setState(ROLLBACK_STATE_DELETED, reason); 765 } 766 767 /** 768 * Returns true if we are expecting the package manager to call restoreUserData for this 769 * rollback because it has just been committed but the rollback has not yet been fully applied. 770 */ 771 @WorkerThread isRestoreUserDataInProgress()772 boolean isRestoreUserDataInProgress() { 773 assertInWorkerThread(); 774 return mRestoreUserDataInProgress; 775 } 776 777 /** 778 * Sets whether we are expecting the package manager to call restoreUserData for this 779 * rollback because it has just been committed but the rollback has not yet been fully applied. 780 */ 781 @WorkerThread setRestoreUserDataInProgress(boolean restoreUserDataInProgress)782 void setRestoreUserDataInProgress(boolean restoreUserDataInProgress) { 783 assertInWorkerThread(); 784 mRestoreUserDataInProgress = restoreUserDataInProgress; 785 RollbackStore.saveRollback(this); 786 } 787 788 /** 789 * Returns true if this rollback includes the package with the provided {@code packageName}. 790 */ 791 @WorkerThread includesPackage(String packageName)792 boolean includesPackage(String packageName) { 793 assertInWorkerThread(); 794 for (PackageRollbackInfo packageRollbackInfo : info.getPackages()) { 795 if (packageRollbackInfo.getPackageName().equals(packageName)) { 796 return true; 797 } 798 } 799 return false; 800 } 801 802 /** 803 * Returns true if this rollback includes the package with the provided {@code packageName} 804 * with a <i>version rolled back from</i> that is not {@code versionCode}. 805 */ 806 @WorkerThread includesPackageWithDifferentVersion(String packageName, long versionCode)807 boolean includesPackageWithDifferentVersion(String packageName, long versionCode) { 808 assertInWorkerThread(); 809 for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { 810 if (pkgRollbackInfo.getPackageName().equals(packageName) 811 && pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode() 812 != versionCode) { 813 return true; 814 } 815 } 816 return false; 817 } 818 819 /** 820 * Returns a list containing the names of all the packages included in this rollback. 821 */ 822 @WorkerThread getPackageNames()823 List<String> getPackageNames() { 824 assertInWorkerThread(); 825 List<String> result = new ArrayList<>(); 826 for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { 827 result.add(pkgRollbackInfo.getPackageName()); 828 } 829 return result; 830 } 831 832 /** 833 * Returns a list containing the names of all the apex packages included in this rollback. 834 */ 835 @WorkerThread getApexPackageNames()836 List<String> getApexPackageNames() { 837 assertInWorkerThread(); 838 List<String> result = new ArrayList<>(); 839 for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { 840 if (pkgRollbackInfo.isApex()) { 841 result.add(pkgRollbackInfo.getPackageName()); 842 } 843 } 844 return result; 845 } 846 847 /** 848 * Returns true if this rollback contains the provided {@code packageSessionId}. 849 */ 850 @AnyThread containsSessionId(int packageSessionId)851 boolean containsSessionId(int packageSessionId) { 852 for (int id : mPackageSessionIds) { 853 if (id == packageSessionId) { 854 return true; 855 } 856 } 857 return false; 858 } 859 860 /** 861 * Returns true if all packages in this rollback are enabled. We won't enable this rollback 862 * until all packages are enabled. Note we don't count apk-in-apex here since they are enabled 863 * automatically when the embedding apex is enabled. 864 */ 865 @WorkerThread allPackagesEnabled()866 boolean allPackagesEnabled() { 867 assertInWorkerThread(); 868 int packagesWithoutApkInApex = 0; 869 for (PackageRollbackInfo rollbackInfo : info.getPackages()) { 870 if (!rollbackInfo.isApkInApex()) { 871 packagesWithoutApkInApex++; 872 } 873 } 874 return packagesWithoutApkInApex == mPackageSessionIds.length; 875 } 876 877 @AnyThread rollbackStateToString(@ollbackState int state)878 static String rollbackStateToString(@RollbackState int state) { 879 switch (state) { 880 case Rollback.ROLLBACK_STATE_ENABLING: return "enabling"; 881 case Rollback.ROLLBACK_STATE_AVAILABLE: return "available"; 882 case Rollback.ROLLBACK_STATE_COMMITTED: return "committed"; 883 case Rollback.ROLLBACK_STATE_DELETED: return "deleted"; 884 } 885 throw new AssertionError("Invalid rollback state: " + state); 886 } 887 888 @AnyThread rollbackStateFromString(String state)889 static @RollbackState int rollbackStateFromString(String state) 890 throws ParseException { 891 switch (state) { 892 case "enabling": return Rollback.ROLLBACK_STATE_ENABLING; 893 case "available": return Rollback.ROLLBACK_STATE_AVAILABLE; 894 case "committed": return Rollback.ROLLBACK_STATE_COMMITTED; 895 case "deleted": return Rollback.ROLLBACK_STATE_DELETED; 896 } 897 throw new ParseException("Invalid rollback state: " + state, 0); 898 } 899 900 @WorkerThread getStateAsString()901 String getStateAsString() { 902 assertInWorkerThread(); 903 return rollbackStateToString(mState); 904 } 905 906 /** 907 * Returns true if there is an app installed that specifies a minExtensionVersion greater 908 * than what was present at the time this Rollback was created. 909 */ 910 @VisibleForTesting extensionVersionReductionWouldViolateConstraint( SparseIntArray rollbackExtVers, PackageManagerInternal pmi)911 static boolean extensionVersionReductionWouldViolateConstraint( 912 SparseIntArray rollbackExtVers, PackageManagerInternal pmi) { 913 if (rollbackExtVers.size() == 0) { 914 return false; 915 } 916 List<String> packages = pmi.getPackageList().getPackageNames(); 917 for (int i = 0; i < packages.size(); i++) { 918 AndroidPackage pkg = pmi.getPackage(packages.get(i)); 919 SparseIntArray minExtVers = pkg.getMinExtensionVersions(); 920 if (minExtVers == null) { 921 continue; 922 } 923 for (int j = 0; j < rollbackExtVers.size(); j++) { 924 int minExt = minExtVers.get(rollbackExtVers.keyAt(j), -1); 925 if (rollbackExtVers.valueAt(j) < minExt) { 926 return true; 927 } 928 } 929 } 930 return false; 931 } 932 933 /** 934 * Returns true if for any SDK version, the extension version recorded at the time of rollback 935 * creation is lower than the current extension version. 936 */ 937 @AnyThread wasCreatedAtLowerExtensionVersion()938 private boolean wasCreatedAtLowerExtensionVersion() { 939 for (int i = 0; i < mExtensionVersions.size(); i++) { 940 if (SdkExtensions.getExtensionVersion(mExtensionVersions.keyAt(i)) 941 > mExtensionVersions.valueAt(i)) { 942 return true; 943 } 944 } 945 return false; 946 } 947 948 @AnyThread containsApex()949 private boolean containsApex() { 950 for (PackageRollbackInfo pkgInfo : info.getPackages()) { 951 if (pkgInfo.isApex()) { 952 return true; 953 } 954 } 955 return false; 956 } 957 958 @WorkerThread dump(IndentingPrintWriter ipw)959 void dump(IndentingPrintWriter ipw) { 960 assertInWorkerThread(); 961 ipw.println(info.getRollbackId() + ":"); 962 ipw.increaseIndent(); 963 ipw.println("-state: " + getStateAsString()); 964 ipw.println("-stateDescription: " + mStateDescription); 965 ipw.println("-timestamp: " + getTimestamp()); 966 ipw.println("-rollbackLifetimeMillis: " + getRollbackLifetimeMillis()); 967 ipw.println("-rollbackImpactLevel: " + info.getRollbackImpactLevel()); 968 ipw.println("-isStaged: " + isStaged()); 969 ipw.println("-originalSessionId: " + getOriginalSessionId()); 970 ipw.println("-packages:"); 971 ipw.increaseIndent(); 972 for (PackageRollbackInfo pkg : info.getPackages()) { 973 ipw.println(pkg.getPackageName() 974 + " " + pkg.getVersionRolledBackFrom().getLongVersionCode() 975 + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode() 976 + " [" + pkg.getRollbackDataPolicy() + "]"); 977 } 978 ipw.decreaseIndent(); 979 if (isCommitted()) { 980 ipw.println("-causePackages:"); 981 ipw.increaseIndent(); 982 for (VersionedPackage cPkg : info.getCausePackages()) { 983 ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode()); 984 } 985 ipw.decreaseIndent(); 986 ipw.println("-committedSessionId: " + info.getCommittedSessionId()); 987 } 988 if (mExtensionVersions.size() > 0) { 989 ipw.println("-extensionVersions:"); 990 ipw.increaseIndent(); 991 ipw.println(mExtensionVersions.toString()); 992 ipw.decreaseIndent(); 993 } 994 ipw.decreaseIndent(); 995 } 996 997 @WorkerThread getStateDescription()998 String getStateDescription() { 999 assertInWorkerThread(); 1000 return mStateDescription; 1001 } 1002 1003 @VisibleForTesting setState(@ollbackState int state, String description)1004 void setState(@RollbackState int state, String description) { 1005 assertInWorkerThread(); 1006 mState = state; 1007 mStateDescription = description; 1008 } 1009 } 1010