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