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