1 /* 2 * Copyright (C) 2019 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.cts.rollback.host.app; 18 19 import static com.android.cts.shim.lib.ShimPackage.PRIVILEGED_SHIM_PACKAGE_NAME; 20 import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME; 21 import static com.android.cts.shim.lib.ShimPackage.SHIM_PACKAGE_NAME; 22 import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import android.Manifest; 27 import android.content.Context; 28 import android.content.pm.PackageInstaller; 29 import android.content.rollback.RollbackInfo; 30 import android.content.rollback.RollbackManager; 31 import android.os.storage.StorageManager; 32 33 import androidx.test.platform.app.InstrumentationRegistry; 34 35 import com.android.cts.install.lib.Install; 36 import com.android.cts.install.lib.InstallUtils; 37 import com.android.cts.install.lib.TestApp; 38 import com.android.cts.rollback.lib.Rollback; 39 import com.android.cts.rollback.lib.RollbackUtils; 40 41 import org.junit.After; 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 import org.junit.runners.JUnit4; 46 47 import java.io.BufferedReader; 48 import java.io.BufferedWriter; 49 import java.io.File; 50 import java.io.FileReader; 51 import java.io.FileWriter; 52 import java.io.IOException; 53 import java.nio.file.Files; 54 55 /** 56 * On-device helper test methods used for host-driven rollback tests. 57 */ 58 @RunWith(JUnit4.class) 59 public class HostTestHelper { 60 private static final String TAG = "RollbackTest"; 61 62 private static final TestApp Apex2SignedBobRotRollback = new TestApp( 63 "Apex2SignedBobRotRollback", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, 64 "com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex"); 65 private static final String APK_VERSION_FILENAME = "ctsrollback_apkversion"; 66 private static final String APK_VERSION_SEPARATOR = ","; 67 68 /** 69 * Adopts common permissions needed to test rollbacks. 70 */ 71 @Before setup()72 public void setup() throws InterruptedException, IOException { 73 InstallUtils.adoptShellPermissionIdentity( 74 Manifest.permission.INSTALL_PACKAGES, 75 Manifest.permission.DELETE_PACKAGES, 76 Manifest.permission.TEST_MANAGE_ROLLBACKS); 77 } 78 79 /** 80 * Drops adopted shell permissions. 81 */ 82 @After teardown()83 public void teardown() throws InterruptedException, IOException { 84 InstallUtils.dropShellPermissionIdentity(); 85 } 86 87 @Test cleanUp()88 public void cleanUp() throws Exception { 89 // Remove all pending rollbacks 90 RollbackManager rm = RollbackUtils.getRollbackManager(); 91 rm.getAvailableRollbacks().stream().flatMap(info -> info.getPackages().stream()) 92 .map(info -> info.getPackageName()).forEach(rm::expireRollbackForPackage); 93 // remove the version file. 94 Files.deleteIfExists(getApkInApexVersionFile().toPath()); 95 } 96 97 /** 98 * Test rollbacks of staged installs involving only apks. 99 * Commits TestApp.A2 as a staged install with rollback enabled. 100 */ 101 @Test testApkOnlyStagedRollback_Phase1_Install()102 public void testApkOnlyStagedRollback_Phase1_Install() throws Exception { 103 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); 104 105 Install.single(TestApp.A1).commit(); 106 Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); 107 } 108 109 /** 110 * Test rollbacks of staged installs involving only apks. 111 * Confirms a staged rollback is available for TestApp.A2 and commits the 112 * rollback. 113 */ 114 @Test testApkOnlyStagedRollback_Phase2_RollBack()115 public void testApkOnlyStagedRollback_Phase2_RollBack() throws Exception { 116 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 117 InstallUtils.processUserData(TestApp.A); 118 119 RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A); 120 assertThat(available).isStaged(); 121 assertThat(available).packagesContainsExactly( 122 Rollback.from(TestApp.A2).to(TestApp.A1)); 123 assertThat(RollbackUtils.getCommittedRollback(TestApp.A)).isNull(); 124 125 RollbackUtils.rollback(available.getRollbackId(), TestApp.A2); 126 RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 127 assertThat(committed).hasRollbackId(available.getRollbackId()); 128 assertThat(committed).isStaged(); 129 assertThat(committed).packagesContainsExactly( 130 Rollback.from(TestApp.A2).to(TestApp.A1)); 131 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 132 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 133 134 // Note: The app is not rolled back until after the rollback is staged 135 // and the device has been rebooted. 136 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 137 } 138 139 /** 140 * Test rollbacks of staged installs involving only apks. 141 * Confirms TestApp.A2 was rolled back. 142 */ 143 @Test testApkOnlyStagedRollback_Phase3_Confirm()144 public void testApkOnlyStagedRollback_Phase3_Confirm() throws Exception { 145 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); 146 InstallUtils.processUserData(TestApp.A); 147 148 RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 149 assertThat(committed).isStaged(); 150 assertThat(committed).packagesContainsExactly( 151 Rollback.from(TestApp.A2).to(TestApp.A1)); 152 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 153 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 154 } 155 156 /** 157 * Test rollbacks of multiple staged installs involving only apks. 158 * Commits TestApp.A2 and TestApp.B2 as a staged install with rollback enabled. 159 */ 160 @Test testApkOnlyMultipleStagedRollback_Phase1_Install()161 public void testApkOnlyMultipleStagedRollback_Phase1_Install() throws Exception { 162 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); 163 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(-1); 164 165 Install.single(TestApp.A1).commit(); 166 Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); 167 168 Install.single(TestApp.B1).commit(); 169 Install.single(TestApp.B2).setStaged().setEnableRollback().commit(); 170 } 171 172 /** 173 * Test rollbacks of multiple staged installs involving only apks. 174 * Confirms staged rollbacks are available for TestApp.A2 and TestApp.b2, and commits the 175 * rollback. 176 */ 177 @Test testApkOnlyMultipleStagedRollback_Phase2_RollBack()178 public void testApkOnlyMultipleStagedRollback_Phase2_RollBack() throws Exception { 179 // Process TestApp.A 180 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 181 InstallUtils.processUserData(TestApp.A); 182 RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A); 183 assertThat(available).isStaged(); 184 assertThat(available).packagesContainsExactly( 185 Rollback.from(TestApp.A2).to(TestApp.A1)); 186 assertThat(RollbackUtils.getCommittedRollback(TestApp.A)).isNull(); 187 188 RollbackUtils.rollback(available.getRollbackId(), TestApp.A2); 189 RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 190 assertThat(committed).hasRollbackId(available.getRollbackId()); 191 assertThat(committed).isStaged(); 192 assertThat(committed).packagesContainsExactly( 193 Rollback.from(TestApp.A2).to(TestApp.A1)); 194 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 195 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 196 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 197 198 // Process TestApp.B 199 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2); 200 InstallUtils.processUserData(TestApp.B); 201 available = RollbackUtils.getAvailableRollback(TestApp.B); 202 assertThat(available).isStaged(); 203 assertThat(available).packagesContainsExactly( 204 Rollback.from(TestApp.B2).to(TestApp.B1)); 205 assertThat(RollbackUtils.getCommittedRollback(TestApp.B)).isNull(); 206 207 RollbackUtils.rollback(available.getRollbackId(), TestApp.B2); 208 committed = RollbackUtils.getCommittedRollback(TestApp.B); 209 assertThat(committed).hasRollbackId(available.getRollbackId()); 210 assertThat(committed).isStaged(); 211 assertThat(committed).packagesContainsExactly( 212 Rollback.from(TestApp.B2).to(TestApp.B1)); 213 assertThat(committed).causePackagesContainsExactly(TestApp.B2); 214 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 215 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 216 } 217 218 /** 219 * Test rollbacks of staged installs involving only apks. 220 * Confirms TestApp.A2 and TestApp.B2 was rolled back. 221 */ 222 @Test testApkOnlyMultipleStagedRollback_Phase3_Confirm()223 public void testApkOnlyMultipleStagedRollback_Phase3_Confirm() throws Exception { 224 // Process TestApp.A 225 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); 226 InstallUtils.processUserData(TestApp.A); 227 RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 228 assertThat(committed).isStaged(); 229 assertThat(committed).packagesContainsExactly( 230 Rollback.from(TestApp.A2).to(TestApp.A1)); 231 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 232 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 233 234 // Process TestApp.B 235 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1); 236 InstallUtils.processUserData(TestApp.B); 237 committed = RollbackUtils.getCommittedRollback(TestApp.B); 238 assertThat(committed).isStaged(); 239 assertThat(committed).packagesContainsExactly( 240 Rollback.from(TestApp.B2).to(TestApp.B1)); 241 assertThat(committed).causePackagesContainsExactly(TestApp.B2); 242 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 243 } 244 245 /** 246 * Test partial rollbacks of multiple staged installs involving only apks. 247 * Commits TestApp.A2 and TestApp.B2 as a staged install with rollback enabled. 248 */ 249 @Test testApkOnlyMultipleStagedPartialRollback_Phase1_Install()250 public void testApkOnlyMultipleStagedPartialRollback_Phase1_Install() throws Exception { 251 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); 252 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(-1); 253 254 Install.single(TestApp.A1).commit(); 255 Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); 256 257 Install.single(TestApp.B1).commit(); 258 Install.single(TestApp.B2).setStaged().commit(); 259 } 260 261 /** 262 * Test partial rollbacks of multiple staged installs involving only apks. 263 * Confirms staged rollbacks are available for TestApp.A2, and commits the 264 * rollback. 265 */ 266 @Test testApkOnlyMultipleStagedPartialRollback_Phase2_RollBack()267 public void testApkOnlyMultipleStagedPartialRollback_Phase2_RollBack() throws Exception { 268 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 269 InstallUtils.processUserData(TestApp.A); 270 RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A); 271 assertThat(available).isStaged(); 272 assertThat(available).packagesContainsExactly( 273 Rollback.from(TestApp.A2).to(TestApp.A1)); 274 assertThat(RollbackUtils.getCommittedRollback(TestApp.A)).isNull(); 275 276 RollbackUtils.rollback(available.getRollbackId(), TestApp.A2); 277 RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 278 assertThat(committed).hasRollbackId(available.getRollbackId()); 279 assertThat(committed).isStaged(); 280 assertThat(committed).packagesContainsExactly( 281 Rollback.from(TestApp.A2).to(TestApp.A1)); 282 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 283 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 284 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 285 } 286 287 /** 288 * Test partial rollbacks of staged installs involving only apks. 289 * Confirms TestApp.A2 was rolled back. 290 */ 291 @Test testApkOnlyMultipleStagedPartialRollback_Phase3_Confirm()292 public void testApkOnlyMultipleStagedPartialRollback_Phase3_Confirm() throws Exception { 293 // Process TestApp.A 294 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); 295 InstallUtils.processUserData(TestApp.A); 296 RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 297 assertThat(committed).isStaged(); 298 assertThat(committed).packagesContainsExactly( 299 Rollback.from(TestApp.A2).to(TestApp.A1)); 300 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 301 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 302 303 // Process TestApp.B 304 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2); 305 } 306 307 /** 308 * Test rollbacks of staged installs involving only apex. 309 * Install first version phase. 310 * 311 * <p> We start by installing version 2. The test ultimately rolls back from 3 to 2. 312 */ 313 @Test testApexOnlyStagedRollback_Phase1_InstallFirst()314 public void testApexOnlyStagedRollback_Phase1_InstallFirst() throws Exception { 315 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 316 Install.single(TestApp.Apex2).setStaged().commit(); 317 } 318 319 /** 320 * Test rollbacks of staged installs involving only apex. 321 * Enable rollback phase. 322 */ 323 @Test testApexOnlyStagedRollback_Phase2_InstallSecond()324 public void testApexOnlyStagedRollback_Phase2_InstallSecond() throws Exception { 325 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 326 327 // keep the versions of the apks in shim apex for verifying in phase3 328 recordApkInApexVersion(); 329 330 Install.single(TestApp.Apex3).setStaged().setEnableRollback().commit(); 331 } 332 333 /** 334 * Test rollbacks of staged installs involving only apex. 335 * Commit rollback phase. 336 */ 337 @Test testApexOnlyStagedRollback_Phase3_RollBack()338 public void testApexOnlyStagedRollback_Phase3_RollBack() throws Exception { 339 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 340 341 long[] versions = retrieveApkInApexVersion(); 342 final long apkInShimApexVersion = versions[0]; 343 final long privApkInShimApexVersion = versions[1]; 344 345 RollbackInfo available = RollbackUtils.getAvailableRollback(SHIM_APEX_PACKAGE_NAME); 346 assertThat(available).isStaged(); 347 assertThat(available).packagesContainsExactly( 348 Rollback.from(TestApp.Apex3).to(TestApp.Apex2), 349 Rollback.from(SHIM_PACKAGE_NAME, 0) 350 .to(SHIM_PACKAGE_NAME, apkInShimApexVersion), 351 Rollback.from(PRIVILEGED_SHIM_PACKAGE_NAME, 0) 352 .to(PRIVILEGED_SHIM_PACKAGE_NAME, privApkInShimApexVersion)); 353 354 RollbackUtils.rollback(available.getRollbackId(), TestApp.Apex3); 355 RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId()); 356 assertThat(committed).isNotNull(); 357 assertThat(committed).isStaged(); 358 assertThat(committed).packagesContainsExactly( 359 Rollback.from(TestApp.Apex3).to(TestApp.Apex2), 360 Rollback.from(SHIM_PACKAGE_NAME, 0) 361 .to(SHIM_PACKAGE_NAME, apkInShimApexVersion), 362 Rollback.from(PRIVILEGED_SHIM_PACKAGE_NAME, 0) 363 .to(PRIVILEGED_SHIM_PACKAGE_NAME, privApkInShimApexVersion)); 364 assertThat(committed).causePackagesContainsExactly(TestApp.Apex3); 365 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 366 367 // Note: The app is not rolled back until after the rollback is staged 368 // and the device has been rebooted. 369 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 370 } 371 372 /** 373 * Test rollbacks of staged installs involving only apex. 374 * Confirm rollback phase. 375 */ 376 @Test testApexOnlyStagedRollback_Phase4_Confirm()377 public void testApexOnlyStagedRollback_Phase4_Confirm() throws Exception { 378 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 379 380 // Rollback data for shim apex will remain in storage since the apex cannot be completely 381 // removed and thus the rollback data won't be expired. Unfortunately, we can't also delete 382 // the rollback data manually from storage. 383 } 384 385 /** 386 * Test rollback to system version involving apex only 387 */ 388 @Test testApexOnlySystemVersionStagedRollback_Phase1_Install()389 public void testApexOnlySystemVersionStagedRollback_Phase1_Install() throws Exception { 390 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 391 392 // keep the versions of the apks in shim apex for verifying in phase2 393 recordApkInApexVersion(); 394 395 Install.single(TestApp.Apex2).setStaged().setEnableRollback().commit(); 396 } 397 398 @Test testApexOnlySystemVersionStagedRollback_Phase2_RollBack()399 public void testApexOnlySystemVersionStagedRollback_Phase2_RollBack() throws Exception { 400 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 401 402 long[] versions = retrieveApkInApexVersion(); 403 final long apkInShimApexVersion = versions[0]; 404 final long privApkInShimApexVersion = versions[1]; 405 406 RollbackInfo available = RollbackUtils.getAvailableRollback(SHIM_APEX_PACKAGE_NAME); 407 assertThat(available).isStaged(); 408 assertThat(available).packagesContainsExactly( 409 Rollback.from(TestApp.Apex2).to(TestApp.Apex1), 410 Rollback.from(SHIM_PACKAGE_NAME, 0) 411 .to(SHIM_PACKAGE_NAME, apkInShimApexVersion), 412 Rollback.from(PRIVILEGED_SHIM_PACKAGE_NAME, 0) 413 .to(PRIVILEGED_SHIM_PACKAGE_NAME, privApkInShimApexVersion)); 414 415 RollbackUtils.rollback(available.getRollbackId(), TestApp.Apex2); 416 RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId()); 417 assertThat(committed).isNotNull(); 418 assertThat(committed).isStaged(); 419 assertThat(committed).packagesContainsExactly( 420 Rollback.from(TestApp.Apex2).to(TestApp.Apex1), 421 Rollback.from(SHIM_PACKAGE_NAME, 0) 422 .to(SHIM_PACKAGE_NAME, apkInShimApexVersion), 423 Rollback.from(PRIVILEGED_SHIM_PACKAGE_NAME, 0) 424 .to(PRIVILEGED_SHIM_PACKAGE_NAME, privApkInShimApexVersion)); 425 assertThat(committed).causePackagesContainsExactly(TestApp.Apex2); 426 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 427 428 // Note: The app is not rolled back until after the rollback is staged 429 // and the device has been rebooted. 430 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 431 } 432 433 @Test testApexOnlySystemVersionStagedRollback_Phase3_Confirm()434 public void testApexOnlySystemVersionStagedRollback_Phase3_Confirm() throws Exception { 435 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 436 } 437 438 /** 439 * Test rollbacks of staged installs involving apex and apk. 440 * Install first version phase. 441 */ 442 @Test testApexAndApkStagedRollback_Phase1_InstallFirst()443 public void testApexAndApkStagedRollback_Phase1_InstallFirst() throws Exception { 444 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 445 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); 446 447 Install.multi(TestApp.Apex2, TestApp.A1).setStaged().commit(); 448 } 449 450 /** 451 * Test rollbacks of staged installs involving apex and apk. 452 * Enable rollback phase. 453 */ 454 @Test testApexAndApkStagedRollback_Phase2_InstallSecond()455 public void testApexAndApkStagedRollback_Phase2_InstallSecond() throws Exception { 456 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 457 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); 458 459 // keep the versions of the apks in shim apex for verifying in phase3 and phase4 460 recordApkInApexVersion(); 461 462 Install.multi(TestApp.Apex3, TestApp.A2).setStaged().setEnableRollback().commit(); 463 } 464 465 /** 466 * Test rollbacks of staged installs involving apex and apk. 467 * Commit rollback phase. 468 */ 469 @Test testApexAndApkStagedRollback_Phase3_RollBack()470 public void testApexAndApkStagedRollback_Phase3_RollBack() throws Exception { 471 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 472 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 473 InstallUtils.processUserData(TestApp.A); 474 475 long[] versions = retrieveApkInApexVersion(); 476 final long apkInShimApexVersion = versions[0]; 477 final long privApkInShimApexVersion = versions[1]; 478 479 RollbackInfo available = RollbackUtils.getAvailableRollback(SHIM_APEX_PACKAGE_NAME); 480 assertThat(available).isStaged(); 481 assertThat(available).packagesContainsExactly( 482 Rollback.from(TestApp.Apex3).to(TestApp.Apex2), 483 Rollback.from(TestApp.A2).to(TestApp.A1), 484 Rollback.from(SHIM_PACKAGE_NAME, 0) 485 .to(SHIM_PACKAGE_NAME, apkInShimApexVersion), 486 Rollback.from(PRIVILEGED_SHIM_PACKAGE_NAME, 0) 487 .to(PRIVILEGED_SHIM_PACKAGE_NAME, privApkInShimApexVersion)); 488 489 RollbackUtils.rollback(available.getRollbackId(), TestApp.Apex3, TestApp.A2); 490 RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 491 assertThat(committed).isNotNull(); 492 assertThat(committed).isStaged(); 493 assertThat(committed).packagesContainsExactly( 494 Rollback.from(TestApp.Apex3).to(TestApp.Apex2), 495 Rollback.from(TestApp.A2).to(TestApp.A1), 496 Rollback.from(SHIM_PACKAGE_NAME, 0) 497 .to(SHIM_PACKAGE_NAME, apkInShimApexVersion), 498 Rollback.from(PRIVILEGED_SHIM_PACKAGE_NAME, 0) 499 .to(PRIVILEGED_SHIM_PACKAGE_NAME, privApkInShimApexVersion)); 500 assertThat(committed).causePackagesContainsExactly(TestApp.Apex3, TestApp.A2); 501 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 502 503 // Note: The app is not rolled back until after the rollback is staged 504 // and the device has been rebooted. 505 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 506 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 507 } 508 509 /** 510 * Test rollbacks of staged installs involving apex and apk. 511 * Confirm rollback phase. 512 */ 513 @Test testApexAndApkStagedRollback_Phase4_Confirm()514 public void testApexAndApkStagedRollback_Phase4_Confirm() throws Exception { 515 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 516 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); 517 InstallUtils.processUserData(TestApp.A); 518 519 long[] versions = retrieveApkInApexVersion(); 520 final long apkInShimApexVersion = versions[0]; 521 final long privApkInShimApexVersion = versions[1]; 522 523 RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 524 assertThat(committed).isStaged(); 525 assertThat(committed).packagesContainsExactly( 526 Rollback.from(TestApp.Apex3).to(TestApp.Apex2), 527 Rollback.from(TestApp.A2).to(TestApp.A1), 528 Rollback.from(SHIM_PACKAGE_NAME, 0) 529 .to(SHIM_PACKAGE_NAME, apkInShimApexVersion), 530 Rollback.from(PRIVILEGED_SHIM_PACKAGE_NAME, 0) 531 .to(PRIVILEGED_SHIM_PACKAGE_NAME, privApkInShimApexVersion)); 532 assertThat(committed).causePackagesContainsExactly(TestApp.Apex3, TestApp.A2); 533 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 534 535 // Rollback data for shim apex will remain in storage since the apex cannot be completely 536 // removed and thus the rollback data won't be expired. Unfortunately, we can't also delete 537 // the rollback data manually from storage due to SEPolicy rules. 538 } 539 540 /** 541 * Tests that apex update expires existing rollbacks for that apex. 542 * Enable rollback phase. 543 */ 544 @Test testApexRollbackExpiration_Phase1_InstallFirst()545 public void testApexRollbackExpiration_Phase1_InstallFirst() throws Exception { 546 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 547 548 Install.single(TestApp.Apex2).setStaged().setEnableRollback().commit(); 549 } 550 551 /** 552 * Tests that apex update expires existing rollbacks for that apex. 553 * Update apex phase. 554 */ 555 @Test testApexRollbackExpiration_Phase2_InstallSecond()556 public void testApexRollbackExpiration_Phase2_InstallSecond() throws Exception { 557 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 558 assertThat(RollbackUtils.getAvailableRollback(SHIM_APEX_PACKAGE_NAME)).isNotNull(); 559 Install.single(TestApp.Apex3).setStaged().commit(); 560 } 561 562 /** 563 * Tests that apex update expires existing rollbacks for that apex. 564 * Confirm expiration phase. 565 */ 566 @Test testApexRollbackExpiration_Phase3_Confirm()567 public void testApexRollbackExpiration_Phase3_Confirm() throws Exception { 568 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 569 assertThat(RollbackUtils.getAvailableRollback(SHIM_APEX_PACKAGE_NAME)).isNull(); 570 } 571 572 /** 573 * Test rollback with key downgrade for apex only 574 */ 575 @Test testApexKeyRotationStagedRollback_Phase1_Install()576 public void testApexKeyRotationStagedRollback_Phase1_Install() throws Exception { 577 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 578 579 // keep the versions of the apks in shim apex for verifying in phase2 580 recordApkInApexVersion(); 581 582 Install.single(Apex2SignedBobRotRollback).setStaged().setEnableRollback().commit(); 583 } 584 585 @Test testApexKeyRotationStagedRollback_Phase2_RollBack()586 public void testApexKeyRotationStagedRollback_Phase2_RollBack() throws Exception { 587 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 588 RollbackInfo available = RollbackUtils.getAvailableRollback(SHIM_APEX_PACKAGE_NAME); 589 long[] versions = retrieveApkInApexVersion(); 590 final long apkInShimApexVersion = versions[0]; 591 final long privApkInShimApexVersion = versions[1]; 592 593 assertThat(available).isStaged(); 594 assertThat(available).packagesContainsExactly( 595 Rollback.from(Apex2SignedBobRotRollback).to(TestApp.Apex1), 596 Rollback.from(SHIM_PACKAGE_NAME, 0) 597 .to(SHIM_PACKAGE_NAME, apkInShimApexVersion), 598 Rollback.from(PRIVILEGED_SHIM_PACKAGE_NAME, 0) 599 .to(PRIVILEGED_SHIM_PACKAGE_NAME, privApkInShimApexVersion)); 600 601 RollbackUtils.rollback(available.getRollbackId(), Apex2SignedBobRotRollback); 602 RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId()); 603 assertThat(committed).isNotNull(); 604 assertThat(committed).isStaged(); 605 assertThat(committed).packagesContainsExactly( 606 Rollback.from(Apex2SignedBobRotRollback).to(TestApp.Apex1), 607 Rollback.from(SHIM_PACKAGE_NAME, 0) 608 .to(SHIM_PACKAGE_NAME, apkInShimApexVersion), 609 Rollback.from(PRIVILEGED_SHIM_PACKAGE_NAME, 0) 610 .to(PRIVILEGED_SHIM_PACKAGE_NAME, privApkInShimApexVersion)); 611 assertThat(committed).causePackagesContainsExactly(Apex2SignedBobRotRollback); 612 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 613 614 // Note: The app is not rolled back until after the rollback is staged 615 // and the device has been rebooted. 616 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 617 } 618 619 @Test testApexKeyRotationStagedRollback_Phase3_Confirm()620 public void testApexKeyRotationStagedRollback_Phase3_Confirm() throws Exception { 621 assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 622 } 623 624 @Test testApkRollbackByAnotherInstaller_Phase1_FirstInstaller()625 public void testApkRollbackByAnotherInstaller_Phase1_FirstInstaller() throws Exception { 626 Install.single(TestApp.A1).commit(); 627 Install.single(TestApp.A2).setEnableRollback().commit(); 628 } 629 630 @Test testFingerprintChange_Phase1_Install()631 public void testFingerprintChange_Phase1_Install() throws Exception { 632 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); 633 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(-1); 634 635 // Both available and enabling rollbacks should be invalidated after fingerprint changes. 636 Install.multi(TestApp.A1, TestApp.B1).commit(); 637 Install.single(TestApp.A2).setEnableRollback().commit(); 638 Install.single(TestApp.B2).setEnableRollback().setStaged().commit(); 639 } 640 641 @Test testFingerprintChange_Phase2_Confirm()642 public void testFingerprintChange_Phase2_Confirm() throws Exception { 643 assertThat(RollbackUtils.getRollbackManager().getAvailableRollbacks()).isEmpty(); 644 } 645 646 @Test testRollbackFailsOtherSessions_Phase1_Install()647 public void testRollbackFailsOtherSessions_Phase1_Install() throws Exception { 648 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); 649 Install.single(TestApp.A1).commit(); 650 Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); 651 } 652 653 @Test testRollbackFailsOtherSessions_Phase2_RollBack()654 public void testRollbackFailsOtherSessions_Phase2_RollBack() throws Exception { 655 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 656 InstallUtils.processUserData(TestApp.A); 657 // Stage session for package A to check if it can block rollback of A 658 final int sessionIdA = Install.single(TestApp.A3).setStaged().setEnableRollback().commit(); 659 660 // Stage another package not related to the rollback 661 Install.single(TestApp.B1).commit(); 662 final int sessionIdB = Install.single(TestApp.B2).setStaged().setEnableRollback().commit(); 663 664 final RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A); 665 RollbackUtils.rollback(available.getRollbackId(), TestApp.A2); 666 final RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 667 assertThat(committed).hasRollbackId(available.getRollbackId()); 668 assertThat(committed).isStaged(); 669 assertThat(committed).packagesContainsExactly( 670 Rollback.from(TestApp.A2).to(TestApp.A1)); 671 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 672 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 673 674 // Assert that blocking staged session is failed 675 final PackageInstaller.SessionInfo sessionA = InstallUtils.getStagedSessionInfo(sessionIdA); 676 assertThat(sessionA).isNotNull(); 677 assertThat(sessionA.isStagedSessionFailed()).isTrue(); 678 679 // Assert that the unrelated staged session is also failed 680 final PackageInstaller.SessionInfo sessionB = InstallUtils.getStagedSessionInfo(sessionIdB); 681 assertThat(sessionB).isNotNull(); 682 assertThat(sessionB.isStagedSessionFailed()).isTrue(); 683 684 // Note: The app is not rolled back until after the rollback is staged 685 // and the device has been rebooted. 686 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 687 } 688 689 @Test testRollbackFailsOtherSessions_Phase3_Confirm()690 public void testRollbackFailsOtherSessions_Phase3_Confirm() throws Exception { 691 // Process TestApp.A 692 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); 693 InstallUtils.processUserData(TestApp.A); 694 final RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 695 assertThat(committed).isStaged(); 696 assertThat(committed).packagesContainsExactly( 697 Rollback.from(TestApp.A2).to(TestApp.A1)); 698 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 699 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 700 701 // Assert that unrelated package was also failed 702 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1); 703 } 704 705 @Test testSimultaneousRollbacksBothSucceed_Phase1_Install()706 public void testSimultaneousRollbacksBothSucceed_Phase1_Install() throws Exception { 707 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); 708 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(-1); 709 Install.single(TestApp.A1).commit(); 710 Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); 711 Install.single(TestApp.B1).commit(); 712 Install.single(TestApp.B2).setStaged().setEnableRollback().commit(); 713 } 714 715 @Test testSimultaneousRollbacksBothSucceed_Phase2_RollBack()716 public void testSimultaneousRollbacksBothSucceed_Phase2_RollBack() throws Exception { 717 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); 718 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2); 719 InstallUtils.processUserData(TestApp.A); 720 InstallUtils.processUserData(TestApp.B); 721 722 final RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A); 723 RollbackUtils.rollback(available.getRollbackId(), TestApp.A2); 724 final RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 725 assertThat(committed).hasRollbackId(available.getRollbackId()); 726 assertThat(committed).isStaged(); 727 assertThat(committed).packagesContainsExactly( 728 Rollback.from(TestApp.A2).to(TestApp.A1)); 729 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 730 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 731 732 final RollbackInfo availableB = RollbackUtils.getAvailableRollback(TestApp.B); 733 RollbackUtils.rollback(availableB.getRollbackId(), TestApp.B2); 734 final RollbackInfo committedB = RollbackUtils.getCommittedRollback(TestApp.B); 735 assertThat(committedB).hasRollbackId(availableB.getRollbackId()); 736 assertThat(committedB).isStaged(); 737 assertThat(committedB).packagesContainsExactly( 738 Rollback.from(TestApp.B2).to(TestApp.B1)); 739 assertThat(committedB).causePackagesContainsExactly(TestApp.B2); 740 assertThat(committedB.getCommittedSessionId()).isNotEqualTo(-1); 741 } 742 743 @Test testSimultaneousRollbacksBothSucceed_Phase3_Confirm()744 public void testSimultaneousRollbacksBothSucceed_Phase3_Confirm() throws Exception { 745 // Process TestApp.A 746 assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); 747 InstallUtils.processUserData(TestApp.A); 748 final RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A); 749 assertThat(committed).isStaged(); 750 assertThat(committed).packagesContainsExactly( 751 Rollback.from(TestApp.A2).to(TestApp.A1)); 752 assertThat(committed).causePackagesContainsExactly(TestApp.A2); 753 assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); 754 755 // Process TestApp.B 756 assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1); 757 InstallUtils.processUserData(TestApp.B); 758 final RollbackInfo committedB = RollbackUtils.getCommittedRollback(TestApp.B); 759 assertThat(committedB).isStaged(); 760 assertThat(committedB).packagesContainsExactly( 761 Rollback.from(TestApp.B2).to(TestApp.B1)); 762 assertThat(committedB).causePackagesContainsExactly(TestApp.B2); 763 assertThat(committedB.getCommittedSessionId()).isNotEqualTo(-1); 764 } 765 766 /** 767 * Record the versions of Apk in shim apex and PrivApk in shim apex 768 * in the order into {@link #APK_VERSION_FILENAME}. 769 * 770 * @see ShimPackage#SHIM_PACKAGE_NAME 771 * @see ShimPackage#PRIVILEGED_SHIM_PACKAGE_NAME 772 */ recordApkInApexVersion()773 private void recordApkInApexVersion() throws Exception { 774 final File versionFile = getApkInApexVersionFile(); 775 776 if (!versionFile.exists()) { 777 versionFile.createNewFile(); 778 } 779 780 final long apkInApexVersion = InstallUtils.getInstalledVersion(SHIM_PACKAGE_NAME); 781 final long privApkInApexVersion = InstallUtils.getInstalledVersion( 782 PRIVILEGED_SHIM_PACKAGE_NAME); 783 784 try (BufferedWriter writer = new BufferedWriter(new FileWriter(versionFile))) { 785 writer.write(apkInApexVersion + APK_VERSION_SEPARATOR + privApkInApexVersion); 786 } 787 } 788 789 /** 790 * Returns the array of the versions of Apk in shim apex and PrivApk in shim apex 791 * in the order from {@link #APK_VERSION_FILENAME}. 792 * 793 * @see ShimPackage#SHIM_PACKAGE_NAME 794 * @see ShimPackage#PRIVILEGED_SHIM_PACKAGE_NAME 795 */ retrieveApkInApexVersion()796 private long[] retrieveApkInApexVersion() throws Exception { 797 final File versionFile = getApkInApexVersionFile(); 798 799 if (!versionFile.exists()) { 800 throw new IllegalStateException("The RollbackTest version file not found"); 801 } 802 803 try (BufferedReader reader = new BufferedReader(new FileReader(versionFile))) { 804 String[] versions = reader.readLine().split(APK_VERSION_SEPARATOR); 805 806 if (versions.length != 2) { 807 throw new IllegalStateException("The RollbackTest version file is wrong."); 808 } 809 return new long[]{Long.parseLong(versions[0]), Long.parseLong(versions[1])}; 810 } 811 } 812 getApkInApexVersionFile()813 private File getApkInApexVersionFile() { 814 final Context context = InstrumentationRegistry.getInstrumentation().getContext(); 815 return new File(context.getFilesDir(), APK_VERSION_FILENAME); 816 } 817 } 818