1 /* <lambda>null2 * Copyright (C) 2020 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.pm.test 18 19 import com.android.internal.util.test.SystemPreparer 20 import com.android.tradefed.device.ITestDevice 21 import com.android.tradefed.device.UserInfo 22 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner 23 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test 24 import com.google.common.truth.Truth.assertThat 25 import org.junit.AfterClass 26 import org.junit.Before 27 import org.junit.ClassRule 28 import org.junit.Rule 29 import org.junit.Test 30 import org.junit.rules.RuleChain 31 import org.junit.rules.TemporaryFolder 32 import org.junit.runner.RunWith 33 import java.io.File 34 import java.util.zip.GZIPOutputStream 35 36 @RunWith(DeviceJUnit4ClassRunner::class) 37 class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() { 38 39 companion object { 40 private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" 41 private const val VERSION_STUB = "PackageManagerTestAppStub.apk" 42 private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" 43 44 /** 45 * How many total users on device to test, including primary. This will clean up any 46 * users created specifically for this test. 47 */ 48 private const val USER_COUNT = 3 49 50 /** 51 * Whether to manually reset state at each test method without rebooting 52 * for faster iterative development. 53 */ 54 private const val DEBUG_NO_REBOOT = false 55 56 @get:ClassRule 57 val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) 58 59 private val parentClassName = SystemStubMultiUserDisableUninstallTest::class.java.simpleName 60 61 private val deviceCompressedFile = 62 HostUtils.makePathForApk("$parentClassName.apk", Partition.PRODUCT).parent 63 .resolve("$parentClassName.apk.gz") 64 65 private val stubFile = 66 HostUtils.makePathForApk("$parentClassName-Stub.apk", Partition.PRODUCT) 67 68 private val secondaryUsers = mutableListOf<Int>() 69 private val usersToRemove = mutableListOf<Int>() 70 private var savedDevice: ITestDevice? = null 71 private var savedPreparer: SystemPreparer? = null 72 73 private fun setUpUsers(device: ITestDevice) { 74 if (this.savedDevice != null) return 75 this.savedDevice = device 76 secondaryUsers.clear() 77 secondaryUsers += device.userInfos.values.map(UserInfo::userId).filterNot { it == 0 } 78 while (secondaryUsers.size < USER_COUNT) { 79 secondaryUsers += device.createUser(parentClassName + secondaryUsers.size) 80 .also { usersToRemove += it } 81 } 82 } 83 84 @JvmStatic 85 @AfterClass 86 fun cleanUp() { 87 savedDevice ?: return 88 89 usersToRemove.forEach { 90 savedDevice?.removeUser(it) 91 } 92 93 savedDevice?.uninstallPackage(TEST_PKG_NAME) 94 savedDevice?.deleteFile(stubFile.parent.toString()) 95 savedDevice?.deleteFile(deviceCompressedFile.parent.toString()) 96 savedDevice?.reboot() 97 savedDevice = null 98 99 if (DEBUG_NO_REBOOT) { 100 savedPreparer?.after() 101 savedPreparer = null 102 } 103 } 104 } 105 106 private val tempFolder = TemporaryFolder() 107 108 // TODO(b/160159215): Use START_STOP rather than FULL once it's fixed. This will drastically 109 // improve pre/post-submit times. 110 private val preparer: SystemPreparer = SystemPreparer(tempFolder, 111 SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device } 112 113 @Rule 114 @JvmField 115 val rules = RuleChain.outerRule(tempFolder).let { 116 if (DEBUG_NO_REBOOT) { 117 it!! 118 } else { 119 it.around(preparer)!! 120 } 121 } 122 123 private var hostCompressedFile: File? = null 124 125 private val previousCodePaths = mutableListOf<String>() 126 127 @Before 128 fun ensureUserAndCompressStubAndInstall() { 129 setUpUsers(device) 130 131 val initialized = hostCompressedFile != null 132 if (!initialized) { 133 hostCompressedFile = tempFolder.newFile() 134 hostCompressedFile!!.outputStream().use { 135 javaClass.classLoader 136 .getResource(VERSION_ONE)!! 137 .openStream() 138 .use { input -> 139 GZIPOutputStream(it).use { output -> 140 input.copyTo(output) 141 } 142 } 143 } 144 } 145 146 device.uninstallPackage(TEST_PKG_NAME) 147 148 if (!initialized || !DEBUG_NO_REBOOT) { 149 savedPreparer = preparer 150 preparer.pushResourceFile(VERSION_STUB, stubFile.toString()) 151 .pushFile(hostCompressedFile, deviceCompressedFile.toString()) 152 .reboot() 153 } 154 155 // This test forces the state to installed/enabled for all users, 156 // since it only tests the uninstall/disable side. 157 installExisting(User.PRIMARY) 158 installExisting(User.SECONDARY) 159 160 ensureEnabled() 161 162 // Ensure data app isn't re-installed multiple times by comparing against the original path 163 val codePath = HostUtils.getCodePaths(device, TEST_PKG_NAME).first() 164 assertThat(codePath).contains("/data/app") 165 assertThat(codePath).contains(TEST_PKG_NAME) 166 167 previousCodePaths.clear() 168 previousCodePaths += codePath 169 170 assertState( 171 primaryInstalled = true, primaryEnabled = true, 172 secondaryInstalled = true, secondaryEnabled = true, 173 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 174 ) 175 } 176 177 @Test 178 fun disablePrimaryFirstAndUninstall() { 179 toggleEnabled(false, User.PRIMARY) 180 181 assertState( 182 primaryInstalled = true, primaryEnabled = false, 183 secondaryInstalled = true, secondaryEnabled = true, 184 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 185 ) 186 187 toggleEnabled(false, User.SECONDARY) 188 189 assertState( 190 primaryInstalled = true, primaryEnabled = false, 191 secondaryInstalled = true, secondaryEnabled = false, 192 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 193 ) 194 195 device.uninstallPackage(TEST_PKG_NAME) 196 197 assertState( 198 primaryInstalled = true, primaryEnabled = false, 199 secondaryInstalled = true, secondaryEnabled = false, 200 codePaths = listOf(CodePath.SYSTEM) 201 ) 202 } 203 204 @Test 205 fun disableSecondaryFirstAndUninstall() { 206 toggleEnabled(false, User.SECONDARY) 207 208 assertState( 209 primaryInstalled = true, primaryEnabled = true, 210 secondaryInstalled = true, secondaryEnabled = false, 211 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 212 ) 213 214 toggleEnabled(false, User.PRIMARY) 215 216 assertState( 217 primaryInstalled = true, primaryEnabled = false, 218 secondaryInstalled = true, secondaryEnabled = false, 219 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 220 ) 221 222 device.uninstallPackage(TEST_PKG_NAME) 223 224 assertState( 225 primaryInstalled = true, primaryEnabled = false, 226 secondaryInstalled = true, secondaryEnabled = false, 227 codePaths = listOf(CodePath.SYSTEM) 228 ) 229 } 230 231 @Test 232 fun disabledUninstalledEnablePrimaryFirst() { 233 toggleEnabled(false, User.PRIMARY) 234 toggleEnabled(false, User.SECONDARY) 235 device.uninstallPackage(TEST_PKG_NAME) 236 237 toggleEnabled(true, User.PRIMARY) 238 239 assertState( 240 primaryInstalled = true, primaryEnabled = true, 241 secondaryInstalled = true, secondaryEnabled = false, 242 codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) 243 ) 244 245 toggleEnabled(true, User.SECONDARY) 246 247 assertState( 248 primaryInstalled = true, primaryEnabled = true, 249 secondaryInstalled = true, secondaryEnabled = true, 250 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 251 ) 252 } 253 254 @Test 255 fun disabledUninstalledEnableSecondaryFirst() { 256 toggleEnabled(false, User.PRIMARY) 257 toggleEnabled(false, User.SECONDARY) 258 device.uninstallPackage(TEST_PKG_NAME) 259 260 toggleEnabled(true, User.SECONDARY) 261 262 assertState( 263 primaryInstalled = true, primaryEnabled = false, 264 secondaryInstalled = true, secondaryEnabled = true, 265 codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) 266 ) 267 268 toggleEnabled(true, User.PRIMARY) 269 270 assertState( 271 primaryInstalled = true, primaryEnabled = true, 272 secondaryInstalled = true, secondaryEnabled = true, 273 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 274 ) 275 } 276 277 @Test 278 fun uninstallPrimaryFirstByUserAndInstallExistingPrimaryFirst() { 279 uninstall(User.PRIMARY) 280 281 assertState( 282 primaryInstalled = false, primaryEnabled = true, 283 secondaryInstalled = true, secondaryEnabled = true, 284 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 285 ) 286 287 uninstall(User.SECONDARY) 288 289 assertState( 290 primaryInstalled = false, primaryEnabled = true, 291 secondaryInstalled = false, secondaryEnabled = true, 292 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 293 ) 294 295 installExisting(User.PRIMARY) 296 297 assertState( 298 primaryInstalled = true, primaryEnabled = true, 299 secondaryInstalled = false, secondaryEnabled = true, 300 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 301 ) 302 303 installExisting(User.SECONDARY) 304 305 assertState( 306 primaryInstalled = true, primaryEnabled = true, 307 secondaryInstalled = true, secondaryEnabled = true, 308 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 309 ) 310 } 311 312 @Test 313 fun uninstallSecondaryFirstByUserAndInstallExistingSecondaryFirst() { 314 uninstall(User.SECONDARY) 315 316 assertState( 317 primaryInstalled = true, primaryEnabled = true, 318 secondaryInstalled = false, secondaryEnabled = true, 319 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 320 ) 321 322 uninstall(User.PRIMARY) 323 324 assertState( 325 primaryInstalled = false, primaryEnabled = true, 326 secondaryInstalled = false, secondaryEnabled = true, 327 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 328 ) 329 330 installExisting(User.SECONDARY) 331 332 assertState( 333 primaryInstalled = false, primaryEnabled = true, 334 secondaryInstalled = true, secondaryEnabled = true, 335 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 336 ) 337 338 installExisting(User.PRIMARY) 339 340 assertState( 341 primaryInstalled = true, primaryEnabled = true, 342 secondaryInstalled = true, secondaryEnabled = true, 343 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 344 ) 345 } 346 347 @Test 348 fun uninstallUpdatesAndEnablePrimaryFirst() { 349 device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") 350 351 assertState( 352 primaryInstalled = true, primaryEnabled = true, 353 secondaryInstalled = true, secondaryEnabled = true, 354 // If any user is enabled when uninstalling updates, /data is re-uncompressed 355 codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) 356 ) 357 358 toggleEnabled(false, User.PRIMARY) 359 toggleEnabled(true, User.PRIMARY) 360 361 assertState( 362 primaryInstalled = true, primaryEnabled = true, 363 secondaryInstalled = true, secondaryEnabled = true, 364 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 365 ) 366 367 // Test enabling secondary to ensure path does not change, even though it's already enabled 368 toggleEnabled(true, User.SECONDARY) 369 370 assertState( 371 primaryInstalled = true, primaryEnabled = true, 372 secondaryInstalled = true, secondaryEnabled = true, 373 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 374 ) 375 } 376 377 @Test 378 fun uninstallUpdatesAndEnableSecondaryFirst() { 379 device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") 380 381 assertState( 382 primaryInstalled = true, primaryEnabled = true, 383 secondaryInstalled = true, secondaryEnabled = true, 384 // If any user is enabled when uninstalling updates, /data is re-uncompressed 385 codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) 386 ) 387 388 toggleEnabled(false, User.PRIMARY) 389 390 toggleEnabled(true, User.SECONDARY) 391 392 assertState( 393 primaryInstalled = true, primaryEnabled = false, 394 secondaryInstalled = true, secondaryEnabled = true, 395 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 396 ) 397 398 toggleEnabled(true, User.PRIMARY) 399 400 assertState( 401 primaryInstalled = true, primaryEnabled = true, 402 secondaryInstalled = true, secondaryEnabled = true, 403 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 404 ) 405 } 406 407 @Test 408 fun disabledUninstallUpdatesAndEnablePrimaryFirst() { 409 toggleEnabled(false, User.PRIMARY) 410 toggleEnabled(false, User.SECONDARY) 411 412 device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") 413 414 assertState( 415 primaryInstalled = true, primaryEnabled = false, 416 secondaryInstalled = true, secondaryEnabled = false, 417 codePaths = listOf(CodePath.SYSTEM) 418 ) 419 420 toggleEnabled(false, User.PRIMARY) 421 toggleEnabled(true, User.PRIMARY) 422 423 assertState( 424 primaryInstalled = true, primaryEnabled = true, 425 secondaryInstalled = true, secondaryEnabled = false, 426 codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) 427 ) 428 429 toggleEnabled(true, User.SECONDARY) 430 431 assertState( 432 primaryInstalled = true, primaryEnabled = true, 433 secondaryInstalled = true, secondaryEnabled = true, 434 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 435 ) 436 } 437 438 @Test 439 fun disabledUninstallUpdatesAndEnableSecondaryFirst() { 440 toggleEnabled(false, User.PRIMARY) 441 toggleEnabled(false, User.SECONDARY) 442 443 device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") 444 445 assertState( 446 primaryInstalled = true, primaryEnabled = false, 447 secondaryInstalled = true, secondaryEnabled = false, 448 codePaths = listOf(CodePath.SYSTEM) 449 ) 450 451 toggleEnabled(false, User.PRIMARY) 452 toggleEnabled(true, User.SECONDARY) 453 454 assertState( 455 primaryInstalled = true, primaryEnabled = false, 456 secondaryInstalled = true, secondaryEnabled = true, 457 codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) 458 ) 459 460 toggleEnabled(true, User.PRIMARY) 461 462 assertState( 463 primaryInstalled = true, primaryEnabled = true, 464 secondaryInstalled = true, secondaryEnabled = true, 465 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 466 ) 467 } 468 469 @Test 470 fun uninstalledUninstallUpdatesAndEnablePrimaryFirst() { 471 uninstall(User.PRIMARY) 472 uninstall(User.SECONDARY) 473 474 device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") 475 476 assertState( 477 primaryInstalled = false, primaryEnabled = true, 478 secondaryInstalled = false, secondaryEnabled = true, 479 codePaths = listOf(CodePath.SYSTEM) 480 ) 481 482 toggleEnabled(false, User.PRIMARY) 483 toggleEnabled(true, User.PRIMARY) 484 485 assertState( 486 primaryInstalled = false, primaryEnabled = true, 487 secondaryInstalled = false, secondaryEnabled = true, 488 codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) 489 ) 490 491 toggleEnabled(true, User.SECONDARY) 492 493 assertState( 494 primaryInstalled = false, primaryEnabled = true, 495 secondaryInstalled = false, secondaryEnabled = true, 496 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 497 ) 498 } 499 500 @Test 501 fun uninstalledUninstallUpdatesAndEnableSecondaryFirst() { 502 uninstall(User.PRIMARY) 503 uninstall(User.SECONDARY) 504 505 device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") 506 507 assertState( 508 primaryInstalled = false, primaryEnabled = true, 509 secondaryInstalled = false, secondaryEnabled = true, 510 codePaths = listOf(CodePath.SYSTEM) 511 ) 512 513 toggleEnabled(true, User.SECONDARY) 514 515 assertState( 516 primaryInstalled = false, primaryEnabled = true, 517 secondaryInstalled = false, secondaryEnabled = true, 518 codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) 519 ) 520 521 toggleEnabled(true, User.PRIMARY) 522 523 assertState( 524 primaryInstalled = false, primaryEnabled = true, 525 secondaryInstalled = false, secondaryEnabled = true, 526 codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) 527 ) 528 } 529 530 private fun ensureEnabled() { 531 toggleEnabled(true, User.PRIMARY) 532 toggleEnabled(true, User.SECONDARY) 533 534 assertThat(HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME).all { it.value }) 535 .isTrue() 536 } 537 538 private fun toggleEnabled(enabled: Boolean, user: User, pkgName: String = TEST_PKG_NAME) { 539 val command = if (enabled) "enable" else "disable" 540 @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { 541 User.PRIMARY -> { 542 device.executeShellCommand("pm $command --user 0 $pkgName") 543 } 544 User.SECONDARY -> { 545 secondaryUsers.forEach { 546 device.executeShellCommand("pm $command --user $it $pkgName") 547 } 548 } 549 } 550 } 551 552 private fun uninstall(user: User, pkgName: String = TEST_PKG_NAME) { 553 @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { 554 User.PRIMARY -> { 555 device.executeShellCommand("pm uninstall --user 0 $pkgName") 556 } 557 User.SECONDARY -> { 558 secondaryUsers.forEach { 559 device.executeShellCommand("pm uninstall --user $it $pkgName") 560 } 561 } 562 } 563 } 564 565 private fun installExisting(user: User, pkgName: String = TEST_PKG_NAME) { 566 @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { 567 User.PRIMARY -> { 568 device.executeShellCommand("pm install-existing --user 0 $pkgName") 569 } 570 User.SECONDARY -> { 571 secondaryUsers.forEach { 572 device.executeShellCommand("pm install-existing --user $it $pkgName") 573 } 574 } 575 } 576 } 577 578 private fun assertState( 579 primaryInstalled: Boolean, 580 primaryEnabled: Boolean, 581 secondaryInstalled: Boolean, 582 secondaryEnabled: Boolean, 583 codePaths: List<CodePath> 584 ) { 585 HostUtils.getUserIdToPkgInstalledState(device, TEST_PKG_NAME) 586 .also { assertThat(it.size).isAtLeast(USER_COUNT) } 587 .forEach { (userId, installed) -> 588 if (userId == 0) { 589 assertThat(installed).isEqualTo(primaryInstalled) 590 } else { 591 assertThat(installed).isEqualTo(secondaryInstalled) 592 } 593 } 594 595 HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME) 596 .also { assertThat(it.size).isAtLeast(USER_COUNT) } 597 .forEach { (userId, enabled) -> 598 if (userId == 0) { 599 assertThat(enabled).isEqualTo(primaryEnabled) 600 } else { 601 assertThat(enabled).isEqualTo(secondaryEnabled) 602 } 603 } 604 605 assertCodePaths(codePaths.first(), codePaths.getOrNull(1)) 606 } 607 608 private fun assertCodePaths(firstCodePath: CodePath, secondCodePath: CodePath? = null) { 609 val codePaths = HostUtils.getCodePaths(device, TEST_PKG_NAME) 610 assertThat(codePaths).hasSize(listOfNotNull(firstCodePath, secondCodePath).size) 611 612 when (firstCodePath) { 613 CodePath.SAME -> { 614 assertThat(codePaths[0]).contains("/data/app") 615 assertThat(codePaths[0]).contains(TEST_PKG_NAME) 616 assertThat(codePaths[0]).isEqualTo(previousCodePaths.last()) 617 } 618 CodePath.DIFFERENT -> { 619 assertThat(codePaths[0]).contains("/data/app") 620 assertThat(codePaths[0]).contains(TEST_PKG_NAME) 621 assertThat(previousCodePaths).doesNotContain(codePaths[0]) 622 previousCodePaths.add(codePaths[0]) 623 } 624 CodePath.SYSTEM -> assertThat(codePaths[0]).isEqualTo(stubFile.parent.toString()) 625 } 626 627 when (secondCodePath) { 628 CodePath.SAME, CodePath.DIFFERENT -> 629 throw AssertionError("secondDataPath cannot be a data path") 630 CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString()) 631 null -> {} 632 } 633 } 634 635 enum class User { 636 /** The primary system user 0 */ 637 PRIMARY, 638 639 /** 640 * All other users on the device that are not 0. This is split into an enum so that all 641 * methods that handle secondary act on all non-system users. Some behaviors only occur 642 * if a package state is marked for all non-primary users on the device, which can be 643 * more than just 1. 644 */ 645 SECONDARY 646 } 647 648 enum class CodePath { 649 /** The data code path hasn't changed */ 650 SAME, 651 652 /** New data code path */ 653 DIFFERENT, 654 655 /** The static system code path */ 656 SYSTEM 657 } 658 } 659