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.tests.rollback; 18 19 import static com.android.tests.rollback.RollbackTestUtils.assertPackageRollbackInfoEquals; 20 import static com.android.tests.rollback.RollbackTestUtils.assertRollbackInfoEquals; 21 import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage; 22 import static com.android.tests.rollback.RollbackTestUtils.processUserData; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertNull; 27 import static org.junit.Assert.fail; 28 29 import android.Manifest; 30 import android.content.BroadcastReceiver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.VersionedPackage; 35 import android.content.rollback.RollbackInfo; 36 import android.content.rollback.RollbackManager; 37 import android.provider.DeviceConfig; 38 import android.provider.Settings; 39 import android.util.Log; 40 41 import androidx.test.InstrumentationRegistry; 42 43 import org.junit.Ignore; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.junit.runners.JUnit4; 47 48 import java.util.Collections; 49 import java.util.concurrent.TimeUnit; 50 51 /** 52 * Test system Rollback APIs. 53 * TODO: Should this be a cts test instead? Where should it live? 54 */ 55 @RunWith(JUnit4.class) 56 public class RollbackTest { 57 58 private static final String TAG = "RollbackTest"; 59 60 private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A"; 61 private static final String TEST_APP_B = "com.android.tests.rollback.testapp.B"; 62 private static final String INSTRUMENTED_APP = "com.android.tests.rollback"; 63 64 // copied from PackageManagerService#PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS 65 // TODO: find a better place for the property so that it can be imported in tests 66 // maybe android.content.pm.PackageManager? 67 private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS = 68 "enable_rollback_timeout"; 69 70 /** 71 * Test basic rollbacks. 72 */ 73 @Test testBasic()74 public void testBasic() throws Exception { 75 // Make sure an app can't listen to or disturb the internal 76 // ACTION_PACKAGE_ENABLE_ROLLBACK broadcast. 77 Context context = InstrumentationRegistry.getContext(); 78 IntentFilter enableRollbackFilter = new IntentFilter(); 79 enableRollbackFilter.addAction("android.intent.action.PACKAGE_ENABLE_ROLLBACK"); 80 enableRollbackFilter.addDataType("application/vnd.android.package-archive"); 81 enableRollbackFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 82 BroadcastReceiver enableRollbackReceiver = new BroadcastReceiver() { 83 @Override 84 public void onReceive(Context context, Intent intent) { 85 abortBroadcast(); 86 } 87 }; 88 context.registerReceiver(enableRollbackReceiver, enableRollbackFilter); 89 90 try { 91 RollbackTestUtils.adoptShellPermissionIdentity( 92 Manifest.permission.INSTALL_PACKAGES, 93 Manifest.permission.DELETE_PACKAGES, 94 Manifest.permission.TEST_MANAGE_ROLLBACKS, 95 Manifest.permission.MANAGE_ROLLBACKS); 96 97 // Register a broadcast receiver for notification when the 98 // rollback has been committed. 99 RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver(); 100 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 101 102 // Uninstall TEST_APP_A 103 RollbackTestUtils.uninstall(TEST_APP_A); 104 assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 105 106 // TODO: There is currently a race condition between when the app is 107 // uninstalled and when rollback manager deletes the rollback. Fix it 108 // so that's not the case! 109 for (int i = 0; i < 5; ++i) { 110 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 111 rm.getRecentlyCommittedRollbacks(), TEST_APP_A); 112 if (rollback != null) { 113 Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect."); 114 Thread.sleep(1000); 115 } 116 } 117 118 // The app should not be available for rollback. 119 assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A)); 120 121 // There should be no recently committed rollbacks for this package. 122 assertNull(getUniqueRollbackInfoForPackage( 123 rm.getRecentlyCommittedRollbacks(), TEST_APP_A)); 124 125 // Install v1 of the app (without rollbacks enabled). 126 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 127 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 128 129 // Upgrade from v1 to v2, with rollbacks enabled. 130 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 131 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 132 133 // The app should now be available for rollback. 134 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 135 rm.getAvailableRollbacks(), TEST_APP_A); 136 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); 137 138 // We should not have received any rollback requests yet. 139 // TODO: Possibly flaky if, by chance, some other app on device 140 // happens to be rolled back at the same time? 141 assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS)); 142 143 // Roll back the app. 144 RollbackTestUtils.rollback(rollback.getRollbackId()); 145 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 146 147 // Verify we received a broadcast for the rollback. 148 // TODO: Race condition between the timeout and when the broadcast is 149 // received could lead to test flakiness. 150 Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS); 151 assertNotNull(broadcast); 152 assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS)); 153 154 // Verify the recent rollback has been recorded. 155 rollback = getUniqueRollbackInfoForPackage( 156 rm.getRecentlyCommittedRollbacks(), TEST_APP_A); 157 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); 158 159 broadcastReceiver.unregister(); 160 context.unregisterReceiver(enableRollbackReceiver); 161 } finally { 162 RollbackTestUtils.dropShellPermissionIdentity(); 163 } 164 } 165 166 /** 167 * Test that multiple available rollbacks are properly persisted. 168 */ 169 @Test testAvailableRollbackPersistence()170 public void testAvailableRollbackPersistence() throws Exception { 171 try { 172 RollbackTestUtils.adoptShellPermissionIdentity( 173 Manifest.permission.INSTALL_PACKAGES, 174 Manifest.permission.DELETE_PACKAGES, 175 Manifest.permission.TEST_MANAGE_ROLLBACKS); 176 177 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 178 179 RollbackTestUtils.uninstall(TEST_APP_A); 180 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 181 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 182 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 183 184 RollbackTestUtils.uninstall(TEST_APP_B); 185 RollbackTestUtils.install("RollbackTestAppBv1.apk", false); 186 RollbackTestUtils.install("RollbackTestAppBv2.apk", true); 187 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 188 189 // Both test apps should now be available for rollback. 190 RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( 191 rm.getAvailableRollbacks(), TEST_APP_A); 192 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA); 193 194 RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( 195 rm.getAvailableRollbacks(), TEST_APP_B); 196 assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB); 197 198 // Reload the persisted data. 199 rm.reloadPersistedData(); 200 201 // The apps should still be available for rollback. 202 rollbackA = getUniqueRollbackInfoForPackage( 203 rm.getAvailableRollbacks(), TEST_APP_A); 204 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA); 205 206 rollbackB = getUniqueRollbackInfoForPackage( 207 rm.getAvailableRollbacks(), TEST_APP_B); 208 assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB); 209 210 // Rollback of B should not rollback A 211 RollbackTestUtils.rollback(rollbackB.getRollbackId()); 212 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 213 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 214 } finally { 215 RollbackTestUtils.dropShellPermissionIdentity(); 216 } 217 } 218 219 /** 220 * Test that available multi-package rollbacks are properly persisted. 221 */ 222 @Test testAvailableMultiPackageRollbackPersistence()223 public void testAvailableMultiPackageRollbackPersistence() throws Exception { 224 try { 225 RollbackTestUtils.adoptShellPermissionIdentity( 226 Manifest.permission.INSTALL_PACKAGES, 227 Manifest.permission.DELETE_PACKAGES, 228 Manifest.permission.TEST_MANAGE_ROLLBACKS); 229 230 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 231 232 RollbackTestUtils.uninstall(TEST_APP_A); 233 RollbackTestUtils.uninstall(TEST_APP_B); 234 RollbackTestUtils.installMultiPackage(false, 235 "RollbackTestAppAv1.apk", 236 "RollbackTestAppBv1.apk"); 237 RollbackTestUtils.installMultiPackage(true, 238 "RollbackTestAppAv2.apk", 239 "RollbackTestAppBv2.apk"); 240 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 241 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 242 243 // The app should now be available for rollback. 244 RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( 245 rm.getAvailableRollbacks(), TEST_APP_A); 246 assertRollbackInfoForAandB(rollbackA); 247 248 RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( 249 rm.getAvailableRollbacks(), TEST_APP_B); 250 assertRollbackInfoForAandB(rollbackB); 251 252 // Reload the persisted data. 253 rm.reloadPersistedData(); 254 255 // The apps should still be available for rollback. 256 rollbackA = getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A); 257 assertRollbackInfoForAandB(rollbackA); 258 259 rollbackB = getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_B); 260 assertRollbackInfoForAandB(rollbackB); 261 262 // Rollback of B should rollback A as well 263 RollbackTestUtils.rollback(rollbackB.getRollbackId()); 264 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 265 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 266 } finally { 267 RollbackTestUtils.dropShellPermissionIdentity(); 268 } 269 } 270 271 /** 272 * Test that recently committed rollback data is properly persisted. 273 */ 274 @Test testRecentlyCommittedRollbackPersistence()275 public void testRecentlyCommittedRollbackPersistence() throws Exception { 276 try { 277 RollbackTestUtils.adoptShellPermissionIdentity( 278 Manifest.permission.INSTALL_PACKAGES, 279 Manifest.permission.DELETE_PACKAGES, 280 Manifest.permission.TEST_MANAGE_ROLLBACKS); 281 282 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 283 284 RollbackTestUtils.uninstall(TEST_APP_A); 285 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 286 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 287 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 288 289 // The app should now be available for rollback. 290 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 291 rm.getAvailableRollbacks(), TEST_APP_A); 292 293 // Roll back the app. 294 VersionedPackage cause = new VersionedPackage( 295 "com.android.tests.rollback.testapp.Foo", 42); 296 RollbackTestUtils.rollback(rollback.getRollbackId(), cause); 297 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 298 299 // Verify the recent rollback has been recorded. 300 rollback = getUniqueRollbackInfoForPackage( 301 rm.getRecentlyCommittedRollbacks(), TEST_APP_A); 302 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback, cause); 303 304 // Reload the persisted data. 305 rm.reloadPersistedData(); 306 307 // Verify the recent rollback is still recorded. 308 rollback = getUniqueRollbackInfoForPackage( 309 rm.getRecentlyCommittedRollbacks(), TEST_APP_A); 310 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback, cause); 311 } finally { 312 RollbackTestUtils.dropShellPermissionIdentity(); 313 } 314 } 315 316 /** 317 * Test the scheduling aspect of rollback expiration. 318 */ 319 @Test testRollbackExpiresAfterLifetime()320 public void testRollbackExpiresAfterLifetime() throws Exception { 321 long expirationTime = TimeUnit.SECONDS.toMillis(30); 322 long defaultExpirationTime = TimeUnit.HOURS.toMillis(48); 323 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 324 325 try { 326 RollbackTestUtils.adoptShellPermissionIdentity( 327 Manifest.permission.INSTALL_PACKAGES, 328 Manifest.permission.DELETE_PACKAGES, 329 Manifest.permission.TEST_MANAGE_ROLLBACKS, 330 Manifest.permission.WRITE_DEVICE_CONFIG); 331 332 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT, 333 RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS, 334 Long.toString(expirationTime), false /* makeDefault*/); 335 336 // Pull the new expiration time from DeviceConfig 337 rm.reloadPersistedData(); 338 339 // Uninstall TEST_APP_A 340 RollbackTestUtils.uninstall(TEST_APP_A); 341 assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 342 343 // Install v1 of the app (without rollbacks enabled). 344 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 345 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 346 347 // Upgrade from v1 to v2, with rollbacks enabled. 348 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 349 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 350 351 // Check that the rollback data has not expired 352 Thread.sleep(1000); 353 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 354 rm.getAvailableRollbacks(), TEST_APP_A); 355 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); 356 357 // Give it a little more time, but still not the long enough to expire 358 Thread.sleep(expirationTime / 2); 359 rollback = getUniqueRollbackInfoForPackage( 360 rm.getAvailableRollbacks(), TEST_APP_A); 361 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); 362 363 // Check that the data has expired after the expiration time (with a buffer of 1 second) 364 Thread.sleep(expirationTime / 2); 365 assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A)); 366 367 } finally { 368 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT, 369 RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS, 370 Long.toString(defaultExpirationTime), false /* makeDefault*/); 371 RollbackTestUtils.dropShellPermissionIdentity(); 372 } 373 } 374 375 /** 376 * Test that changing time on device does not affect the duration of time that we keep 377 * rollback available 378 */ 379 @Test testTimeChangeDoesNotAffectLifetime()380 public void testTimeChangeDoesNotAffectLifetime() throws Exception { 381 long expirationTime = TimeUnit.SECONDS.toMillis(30); 382 long defaultExpirationTime = TimeUnit.HOURS.toMillis(48); 383 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 384 385 try { 386 RollbackTestUtils.adoptShellPermissionIdentity( 387 Manifest.permission.INSTALL_PACKAGES, 388 Manifest.permission.DELETE_PACKAGES, 389 Manifest.permission.TEST_MANAGE_ROLLBACKS, 390 Manifest.permission.WRITE_DEVICE_CONFIG, 391 Manifest.permission.SET_TIME); 392 393 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT, 394 RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS, 395 Long.toString(expirationTime), false /* makeDefault*/); 396 397 // Pull the new expiration time from DeviceConfig 398 rm.reloadPersistedData(); 399 400 // Install app A with rollback enabled 401 RollbackTestUtils.uninstall(TEST_APP_A); 402 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 403 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 404 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 405 406 Thread.sleep(expirationTime / 2); 407 408 // Install app B with rollback enabled 409 RollbackTestUtils.uninstall(TEST_APP_B); 410 RollbackTestUtils.install("RollbackTestAppBv1.apk", false); 411 RollbackTestUtils.install("RollbackTestAppBv2.apk", true); 412 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 413 // 1 second buffer 414 Thread.sleep(1000); 415 416 try { 417 // Change the time 418 RollbackTestUtils.forwardTimeBy(expirationTime); 419 420 // 1 second buffer to allow Rollback Manager to handle time change before loading 421 // persisted data 422 Thread.sleep(1000); 423 424 // Load timestamps from storage 425 rm.reloadPersistedData(); 426 427 // Wait until rollback for app A has expired 428 // This will trigger an expiration run that should expire app A but not B 429 Thread.sleep(expirationTime / 2); 430 assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A)); 431 432 // Rollback for app B should not be expired 433 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 434 rm.getAvailableRollbacks(), TEST_APP_B); 435 assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollback); 436 437 // Wait until rollback for app B has expired 438 Thread.sleep(expirationTime / 2); 439 assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_B)); 440 } finally { 441 RollbackTestUtils.forwardTimeBy(-expirationTime); 442 } 443 } finally { 444 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT, 445 RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS, 446 Long.toString(defaultExpirationTime), false /* makeDefault*/); 447 RollbackTestUtils.dropShellPermissionIdentity(); 448 } 449 } 450 451 /** 452 * Test explicit expiration of rollbacks. 453 * Does not test the scheduling aspects of rollback expiration. 454 */ 455 @Test testRollbackExpiration()456 public void testRollbackExpiration() throws Exception { 457 try { 458 RollbackTestUtils.adoptShellPermissionIdentity( 459 Manifest.permission.INSTALL_PACKAGES, 460 Manifest.permission.DELETE_PACKAGES, 461 Manifest.permission.TEST_MANAGE_ROLLBACKS); 462 463 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 464 RollbackTestUtils.uninstall(TEST_APP_A); 465 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 466 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 467 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 468 469 // The app should now be available for rollback. 470 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 471 rm.getAvailableRollbacks(), TEST_APP_A); 472 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); 473 474 // Expire the rollback. 475 rm.expireRollbackForPackage(TEST_APP_A); 476 477 // The rollback should no longer be available. 478 assertNull(getUniqueRollbackInfoForPackage( 479 rm.getAvailableRollbacks(), TEST_APP_A)); 480 } finally { 481 RollbackTestUtils.dropShellPermissionIdentity(); 482 } 483 } 484 485 /** 486 * Test that app user data is rolled back. 487 */ 488 @Test testUserDataRollback()489 public void testUserDataRollback() throws Exception { 490 try { 491 RollbackTestUtils.adoptShellPermissionIdentity( 492 Manifest.permission.INSTALL_PACKAGES, 493 Manifest.permission.DELETE_PACKAGES, 494 Manifest.permission.TEST_MANAGE_ROLLBACKS); 495 496 RollbackTestUtils.uninstall(TEST_APP_A); 497 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 498 processUserData(TEST_APP_A); 499 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 500 processUserData(TEST_APP_A); 501 502 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 503 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 504 rm.getAvailableRollbacks(), TEST_APP_A); 505 RollbackTestUtils.rollback(rollback.getRollbackId()); 506 processUserData(TEST_APP_A); 507 } finally { 508 RollbackTestUtils.dropShellPermissionIdentity(); 509 } 510 } 511 512 /** 513 * Test rollback of apks involving splits. 514 */ 515 @Test testRollbackWithSplits()516 public void testRollbackWithSplits() throws Exception { 517 try { 518 RollbackTestUtils.adoptShellPermissionIdentity( 519 Manifest.permission.INSTALL_PACKAGES, 520 Manifest.permission.DELETE_PACKAGES, 521 Manifest.permission.TEST_MANAGE_ROLLBACKS); 522 523 RollbackTestUtils.uninstall(TEST_APP_A); 524 RollbackTestUtils.installSplit(false, 525 "RollbackTestAppASplitV1.apk", 526 "RollbackTestAppASplitV1_anydpi.apk"); 527 processUserData(TEST_APP_A); 528 529 RollbackTestUtils.installSplit(true, 530 "RollbackTestAppASplitV2.apk", 531 "RollbackTestAppASplitV2_anydpi.apk"); 532 processUserData(TEST_APP_A); 533 534 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 535 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 536 rm.getAvailableRollbacks(), TEST_APP_A); 537 assertNotNull(rollback); 538 RollbackTestUtils.rollback(rollback.getRollbackId()); 539 processUserData(TEST_APP_A); 540 } finally { 541 RollbackTestUtils.dropShellPermissionIdentity(); 542 } 543 } 544 545 /** 546 * Test restrictions on rollback broadcast sender. 547 * A random app should not be able to send a ROLLBACK_COMMITTED broadcast. 548 */ 549 @Test testRollbackBroadcastRestrictions()550 public void testRollbackBroadcastRestrictions() throws Exception { 551 RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver(); 552 Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED); 553 try { 554 InstrumentationRegistry.getContext().sendBroadcast(broadcast); 555 fail("Succeeded in sending restricted broadcast from app context."); 556 } catch (SecurityException se) { 557 // Expected behavior. 558 } 559 560 // Confirm that we really haven't received the broadcast. 561 // TODO: How long to wait for the expected timeout? 562 assertNull(broadcastReceiver.poll(5, TimeUnit.SECONDS)); 563 564 // TODO: Do we need to do this? Do we need to ensure this is always 565 // called, even when the test fails? 566 broadcastReceiver.unregister(); 567 } 568 569 /** 570 * Regression test for rollback in the case when multiple apps are 571 * available for rollback at the same time. 572 */ 573 @Test testMultipleRollbackAvailable()574 public void testMultipleRollbackAvailable() throws Exception { 575 try { 576 RollbackTestUtils.adoptShellPermissionIdentity( 577 Manifest.permission.INSTALL_PACKAGES, 578 Manifest.permission.DELETE_PACKAGES, 579 Manifest.permission.TEST_MANAGE_ROLLBACKS); 580 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 581 582 // Prep installation of the test apps. 583 RollbackTestUtils.uninstall(TEST_APP_A); 584 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 585 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 586 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 587 588 RollbackTestUtils.uninstall(TEST_APP_B); 589 RollbackTestUtils.install("RollbackTestAppBv1.apk", false); 590 RollbackTestUtils.install("RollbackTestAppBv2.apk", true); 591 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 592 593 // Both test apps should now be available for rollback, and the 594 // RollbackInfo returned for the rollbacks should be correct. 595 RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( 596 rm.getAvailableRollbacks(), TEST_APP_A); 597 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA); 598 599 RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( 600 rm.getAvailableRollbacks(), TEST_APP_B); 601 assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB); 602 603 // Executing rollback should roll back the correct package. 604 RollbackTestUtils.rollback(rollbackA.getRollbackId()); 605 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 606 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 607 608 RollbackTestUtils.uninstall(TEST_APP_A); 609 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 610 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 611 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 612 613 RollbackTestUtils.rollback(rollbackB.getRollbackId()); 614 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 615 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 616 } finally { 617 RollbackTestUtils.dropShellPermissionIdentity(); 618 } 619 } 620 621 /** 622 * Test that the MANAGE_ROLLBACKS permission is required to call 623 * RollbackManager APIs. 624 */ 625 @Test testManageRollbacksPermission()626 public void testManageRollbacksPermission() throws Exception { 627 // We shouldn't be allowed to call any of the RollbackManager APIs 628 // without the MANAGE_ROLLBACKS permission. 629 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 630 631 try { 632 rm.getAvailableRollbacks(); 633 fail("expected SecurityException"); 634 } catch (SecurityException e) { 635 // Expected. 636 } 637 638 try { 639 rm.getRecentlyCommittedRollbacks(); 640 fail("expected SecurityException"); 641 } catch (SecurityException e) { 642 // Expected. 643 } 644 645 try { 646 // TODO: What if the implementation checks arguments for non-null 647 // first? Then this test isn't valid. 648 rm.commitRollback(0, Collections.emptyList(), null); 649 fail("expected SecurityException"); 650 } catch (SecurityException e) { 651 // Expected. 652 } 653 654 try { 655 rm.reloadPersistedData(); 656 fail("expected SecurityException"); 657 } catch (SecurityException e) { 658 // Expected. 659 } 660 661 try { 662 rm.expireRollbackForPackage(TEST_APP_A); 663 fail("expected SecurityException"); 664 } catch (SecurityException e) { 665 // Expected. 666 } 667 } 668 669 /** 670 * Test that you cannot enable rollback for a package without the 671 * MANAGE_ROLLBACKS permission. 672 */ 673 @Test testEnableRollbackPermission()674 public void testEnableRollbackPermission() throws Exception { 675 try { 676 RollbackTestUtils.adoptShellPermissionIdentity( 677 Manifest.permission.INSTALL_PACKAGES, 678 Manifest.permission.DELETE_PACKAGES); 679 680 RollbackTestUtils.uninstall(TEST_APP_A); 681 RollbackTestUtils.install("RollbackTestAppAv1.apk", /* enableRollback */ false); 682 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 683 684 RollbackTestUtils.install("RollbackTestAppAv2.apk", /* enableRollback */ true); 685 686 // We expect v2 of the app was installed, but rollback has not 687 // been enabled. 688 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 689 690 RollbackTestUtils.adoptShellPermissionIdentity( 691 Manifest.permission.TEST_MANAGE_ROLLBACKS); 692 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 693 assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A)); 694 } finally { 695 RollbackTestUtils.dropShellPermissionIdentity(); 696 } 697 } 698 699 /** 700 * Test that you cannot enable rollback for a non-module package when 701 * holding the MANAGE_ROLLBACKS permission. 702 */ 703 @Test testNonModuleEnableRollback()704 public void testNonModuleEnableRollback() throws Exception { 705 try { 706 RollbackTestUtils.adoptShellPermissionIdentity( 707 Manifest.permission.INSTALL_PACKAGES, 708 Manifest.permission.DELETE_PACKAGES, 709 Manifest.permission.MANAGE_ROLLBACKS); 710 711 RollbackTestUtils.uninstall(TEST_APP_A); 712 RollbackTestUtils.install("RollbackTestAppAv1.apk", /* enableRollback */ false); 713 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 714 715 RollbackTestUtils.install("RollbackTestAppAv2.apk", /* enableRollback */ true); 716 717 // We expect v2 of the app was installed, but rollback has not 718 // been enabled because the test app is not a module. 719 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 720 721 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 722 assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A)); 723 } finally { 724 RollbackTestUtils.dropShellPermissionIdentity(); 725 } 726 } 727 728 /** 729 * Test rollback of multi-package installs is implemented. 730 */ 731 @Test testMultiPackage()732 public void testMultiPackage() throws Exception { 733 try { 734 RollbackTestUtils.adoptShellPermissionIdentity( 735 Manifest.permission.INSTALL_PACKAGES, 736 Manifest.permission.DELETE_PACKAGES, 737 Manifest.permission.TEST_MANAGE_ROLLBACKS); 738 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 739 740 // Prep installation of the test apps. 741 RollbackTestUtils.uninstall(TEST_APP_A); 742 RollbackTestUtils.uninstall(TEST_APP_B); 743 RollbackTestUtils.installMultiPackage(false, 744 "RollbackTestAppAv1.apk", 745 "RollbackTestAppBv1.apk"); 746 processUserData(TEST_APP_A); 747 processUserData(TEST_APP_B); 748 RollbackTestUtils.installMultiPackage(true, 749 "RollbackTestAppAv2.apk", 750 "RollbackTestAppBv2.apk"); 751 processUserData(TEST_APP_A); 752 processUserData(TEST_APP_B); 753 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 754 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 755 756 // TEST_APP_A should now be available for rollback. 757 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 758 rm.getAvailableRollbacks(), TEST_APP_A); 759 assertRollbackInfoForAandB(rollback); 760 761 // Rollback the app. It should cause both test apps to be rolled 762 // back. 763 RollbackTestUtils.rollback(rollback.getRollbackId()); 764 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 765 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 766 767 // We should see recent rollbacks listed for both A and B. 768 Thread.sleep(1000); 769 RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( 770 rm.getRecentlyCommittedRollbacks(), TEST_APP_A); 771 772 RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( 773 rm.getRecentlyCommittedRollbacks(), TEST_APP_B); 774 assertRollbackInfoForAandB(rollbackB); 775 776 assertEquals(rollbackA.getRollbackId(), rollbackB.getRollbackId()); 777 778 processUserData(TEST_APP_A); 779 processUserData(TEST_APP_B); 780 } finally { 781 RollbackTestUtils.dropShellPermissionIdentity(); 782 } 783 } 784 785 /** 786 * Test failure to enable rollback for multi-package installs. 787 * If any one of the packages fail to enable rollback, we shouldn't enable 788 * rollback for any package. 789 */ 790 @Test testMultiPackageEnableFail()791 public void testMultiPackageEnableFail() throws Exception { 792 try { 793 RollbackTestUtils.adoptShellPermissionIdentity( 794 Manifest.permission.INSTALL_PACKAGES, 795 Manifest.permission.DELETE_PACKAGES, 796 Manifest.permission.TEST_MANAGE_ROLLBACKS); 797 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 798 799 RollbackTestUtils.uninstall(TEST_APP_A); 800 RollbackTestUtils.uninstall(TEST_APP_B); 801 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 802 803 // We should fail to enable rollback here because TestApp B is not 804 // already installed. 805 RollbackTestUtils.installMultiPackage(true, 806 "RollbackTestAppAv2.apk", 807 "RollbackTestAppBv2.apk"); 808 809 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 810 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 811 812 assertNull(getUniqueRollbackInfoForPackage( 813 rm.getAvailableRollbacks(), TEST_APP_A)); 814 assertNull(getUniqueRollbackInfoForPackage( 815 rm.getAvailableRollbacks(), TEST_APP_B)); 816 } finally { 817 RollbackTestUtils.dropShellPermissionIdentity(); 818 } 819 } 820 821 @Test 822 @Ignore("b/120200473") 823 /** 824 * Test rollback when app is updated to its same version. 825 */ testSameVersionUpdate()826 public void testSameVersionUpdate() throws Exception { 827 try { 828 RollbackTestUtils.adoptShellPermissionIdentity( 829 Manifest.permission.INSTALL_PACKAGES, 830 Manifest.permission.DELETE_PACKAGES, 831 Manifest.permission.TEST_MANAGE_ROLLBACKS); 832 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 833 834 RollbackTestUtils.uninstall(TEST_APP_A); 835 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 836 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 837 RollbackTestUtils.install("RollbackTestAppACrashingV2.apk", true); 838 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 839 840 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 841 rm.getAvailableRollbacks(), TEST_APP_A); 842 assertRollbackInfoEquals(TEST_APP_A, 2, 2, rollback); 843 844 RollbackTestUtils.rollback(rollback.getRollbackId()); 845 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 846 847 rollback = getUniqueRollbackInfoForPackage( 848 rm.getRecentlyCommittedRollbacks(), TEST_APP_A); 849 assertRollbackInfoEquals(TEST_APP_A, 2, 2, rollback); 850 } finally { 851 RollbackTestUtils.dropShellPermissionIdentity(); 852 } 853 } 854 855 /** 856 * Test bad update automatic rollback. 857 */ 858 @Test testBadUpdateRollback()859 public void testBadUpdateRollback() throws Exception { 860 BroadcastReceiver crashCountReceiver = null; 861 Context context = InstrumentationRegistry.getContext(); 862 try { 863 RollbackTestUtils.adoptShellPermissionIdentity( 864 Manifest.permission.INSTALL_PACKAGES, 865 Manifest.permission.DELETE_PACKAGES, 866 Manifest.permission.MANAGE_ROLLBACKS, 867 Manifest.permission.TEST_MANAGE_ROLLBACKS, 868 Manifest.permission.KILL_BACKGROUND_PROCESSES, 869 Manifest.permission.RESTART_PACKAGES); 870 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 871 872 // Prep installation of the test apps. 873 RollbackTestUtils.uninstall(TEST_APP_A); 874 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 875 RollbackTestUtils.install("RollbackTestAppACrashingV2.apk", true); 876 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 877 878 RollbackTestUtils.uninstall(TEST_APP_B); 879 RollbackTestUtils.install("RollbackTestAppBv1.apk", false); 880 RollbackTestUtils.install("RollbackTestAppBv2.apk", true); 881 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 882 883 // Both test apps should now be available for rollback, and the 884 // targetPackage returned for rollback should be correct. 885 RollbackInfo rollbackA = getUniqueRollbackInfoForPackage( 886 rm.getAvailableRollbacks(), TEST_APP_A); 887 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollbackA); 888 889 RollbackInfo rollbackB = getUniqueRollbackInfoForPackage( 890 rm.getAvailableRollbacks(), TEST_APP_B); 891 assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB); 892 893 // Register rollback committed receiver 894 RollbackBroadcastReceiver rollbackReceiver = new RollbackBroadcastReceiver(); 895 896 // Crash TEST_APP_A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback 897 crashCountReceiver = RollbackTestUtils.sendCrashBroadcast(context, TEST_APP_A, 5); 898 899 // Verify we received a broadcast for the rollback. 900 rollbackReceiver.take(); 901 902 // TEST_APP_A is automatically rolled back by the RollbackPackageHealthObserver 903 assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 904 // Instrumented app is still the package installer 905 String installer = context.getPackageManager().getInstallerPackageName(TEST_APP_A); 906 assertEquals(INSTRUMENTED_APP, installer); 907 // TEST_APP_B is untouched 908 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); 909 } finally { 910 RollbackTestUtils.dropShellPermissionIdentity(); 911 if (crashCountReceiver != null) { 912 context.unregisterReceiver(crashCountReceiver); 913 } 914 } 915 } 916 917 /** 918 * Test race between roll back and roll forward. 919 */ 920 @Test testRollForwardRace()921 public void testRollForwardRace() throws Exception { 922 try { 923 RollbackTestUtils.adoptShellPermissionIdentity( 924 Manifest.permission.INSTALL_PACKAGES, 925 Manifest.permission.DELETE_PACKAGES, 926 Manifest.permission.TEST_MANAGE_ROLLBACKS, 927 Manifest.permission.MANAGE_ROLLBACKS); 928 929 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 930 931 RollbackTestUtils.uninstall(TEST_APP_A); 932 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 933 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 934 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 935 936 RollbackInfo rollback = getUniqueRollbackInfoForPackage( 937 rm.getAvailableRollbacks(), TEST_APP_A); 938 assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); 939 940 // Install a new version of package A, then immediately rollback 941 // the previous version. We expect the rollback to fail, because 942 // it is no longer available. 943 // There are a couple different ways this could fail depending on 944 // thread interleaving, so don't ignore flaky failures. 945 RollbackTestUtils.install("RollbackTestAppAv3.apk", false); 946 try { 947 RollbackTestUtils.rollback(rollback.getRollbackId()); 948 // Note: Don't ignore flaky failures here. 949 fail("Expected rollback to fail, but it did not."); 950 } catch (AssertionError e) { 951 Log.i(TAG, "Note expected failure: ", e); 952 // Expected 953 } 954 955 // Note: Don't ignore flaky failures here. 956 assertEquals(3, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 957 } finally { 958 RollbackTestUtils.dropShellPermissionIdentity(); 959 } 960 } 961 962 @Test testEnableRollbackTimeoutFailsRollback()963 public void testEnableRollbackTimeoutFailsRollback() throws Exception { 964 try { 965 RollbackTestUtils.adoptShellPermissionIdentity( 966 Manifest.permission.INSTALL_PACKAGES, 967 Manifest.permission.DELETE_PACKAGES, 968 Manifest.permission.TEST_MANAGE_ROLLBACKS, 969 Manifest.permission.MANAGE_ROLLBACKS, 970 Manifest.permission.WRITE_DEVICE_CONFIG); 971 972 //setting the timeout to a very short amount that will definitely be triggered 973 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 974 PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS, 975 Long.toString(1), false /* makeDefault*/); 976 RollbackManager rm = RollbackTestUtils.getRollbackManager(); 977 978 RollbackTestUtils.uninstall(TEST_APP_A); 979 RollbackTestUtils.install("RollbackTestAppAv1.apk", false); 980 RollbackTestUtils.install("RollbackTestAppAv2.apk", true); 981 982 assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); 983 984 assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A)); 985 } finally { 986 //setting the timeout back to default 987 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 988 PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS, 989 null, false /* makeDefault*/); 990 RollbackTestUtils.dropShellPermissionIdentity(); 991 } 992 } 993 994 // Helper function to test that the given rollback info is a rollback for 995 // the atomic set {A2, B2} -> {A1, B1}. assertRollbackInfoForAandB(RollbackInfo rollback)996 private void assertRollbackInfoForAandB(RollbackInfo rollback) { 997 assertNotNull(rollback); 998 assertEquals(2, rollback.getPackages().size()); 999 if (TEST_APP_A.equals(rollback.getPackages().get(0).getPackageName())) { 1000 assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(0)); 1001 assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollback.getPackages().get(1)); 1002 } else { 1003 assertPackageRollbackInfoEquals(TEST_APP_B, 2, 1, rollback.getPackages().get(0)); 1004 assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(1)); 1005 } 1006 } 1007 } 1008